/*
** Copyright (c) 2007-2015 by Silicon Laboratories
**
** $Id: si3226_intf.c 4907 2015-04-23 19:03:18Z nizajerk $
**
** SI3226 ProSLIC interface implementation file
**
** Author(s): 
** laj
**
** Distributed by: 
** Silicon Laboratories, Inc
**
** This file contains proprietary information.	 
** No dissemination allowed without prior written permission from
** Silicon Laboratories, Inc.
**
** File Description:
** This is the implementation file for the main ProSLIC API and is used 
** in the ProSLIC demonstration code. 
**
*/

#include "si_voice_datatypes.h"
#include "si_voice_ctrl.h"
#include "si_voice_timer_intf.h"
#include "proslic.h"
#include "si3226_intf.h"
#include "si3226.h"
#include "si3226_registers.h"
#include "proslic_api_config.h"

#define PRAM_ADDR (334 + 0x400)
#define PRAM_DATA (335 + 0x400)

#define WriteReg	pProslic->deviceId->ctrlInterface->WriteRegister_fptr
#define ReadReg		pProslic->deviceId->ctrlInterface->ReadRegister_fptr
#define pProHW		pProslic->deviceId->ctrlInterface->hCtrl
#define Reset		pProslic->deviceId->ctrlInterface->Reset_fptr
#define Delay		pProslic->deviceId->ctrlInterface->Delay_fptr
#define pProTimer	pProslic->deviceId->ctrlInterface->hTimer
#define WriteRAM	pProslic->deviceId->ctrlInterface->WriteRAM_fptr
#define ReadRAM		pProslic->deviceId->ctrlInterface->ReadRAM_fptr
#define TimeElapsed pProslic->deviceId->ctrlInterface->timeElapsed_fptr
#define SetSemaphore pProslic->deviceId->ctrlInterface->Semaphore_fptr

#define WriteRegX		deviceId->ctrlInterface->WriteRegister_fptr
#define ReadRegX		deviceId->ctrlInterface->ReadRegister_fptr
#define pProHWX			deviceId->ctrlInterface->hCtrl
#define DelayX			deviceId->ctrlInterface->Delay_fptr
#define pProTimerX		deviceId->ctrlInterface->hTimer
#define WriteRAMX		deviceId->ctrlInterface->WriteRAM_fptr
#define ReadRAMX		deviceId->ctrlInterface->ReadRAM_fptr

#define BROADCAST 0xff

/*
** Externs
*/

/* General Configuration */
extern Si3226_General_Cfg Si3226_General_Configuration;
#ifdef SIVOICE_MULTI_BOM_SUPPORT
extern const proslicPatch SI3226_PATCH_C_FLBK;
extern const proslicPatch SI3226_PATCH_C_QCUK;
extern const proslicPatch SI3226_PATCH_D_FLBK;
extern const proslicPatch SI3226_PATCH_D_QCUK;
extern const proslicPatch SI3226_PATCH_E_FLBK;
extern Si3226_General_Cfg Si3226_General_Configuration_MultiBOM[];
extern int si3226_genconf_multi_max_preset;
#else
extern const proslicPatch SI3226_PATCH_C_DEFAULT;
extern const proslicPatch SI3226_PATCH_D_DEFAULT;
extern const proslicPatch SI3226_PATCH_E_DEFAULT;
#endif

/* Ringing */
#ifndef DISABLE_RING_SETUP
extern Si3226_Ring_Cfg Si3226_Ring_Presets[];
#endif

/* Tone Generation */
#ifndef DISABLE_TONE_SETUP
extern Si3226_Tone_Cfg Si3226_Tone_Presets[];
#endif

/* FSK */
#ifndef DISABLE_FSK_SETUP
extern Si3226_FSK_Cfg Si3226_FSK_Presets[];
#endif

/* DTMF */
#ifndef DISABLE_DTMF_SETUP
extern Si3226_DTMFDec_Cfg Si3226_DTMFDec_Presets[];
#endif

/* Zsynth */
#ifndef DISABLE_ZSYNTH_SETUP
extern Si3226_Impedance_Cfg Si3226_Impedance_Presets [];
#endif

/* CI/GCI */
#ifndef DISABLE_CI_SETUP
extern Si3226_CI_Cfg Si3226_CI_Presets [];
#endif

/* Audio Gain Scratch */
extern Si3226_audioGain_Cfg Si3226_audioGain_Presets[];

/* DC Feed */
#ifndef DISABLE_DCFEED_SETUP
extern Si3226_DCfeed_Cfg Si3226_DCfeed_Presets[];
#endif

/* GPIO */
#ifndef DISABLE_GPIO_SETUP
extern Si3226_GPIO_Cfg Si3226_GPIO_Configuration ;
#endif

/* PCM */
#ifndef DISABLE_PCM_SETUP
extern Si3226_PCM_Cfg Si3226_PCM_Presets [];
#endif

#ifdef ENABLE_DEBUG
static const char LOGPRINT_PREFIX[] = "Si322x: ";
#endif

/*
** Local functions are defined first
*/

/*
** Function: probeDaisyChain
**
** Description: 
** Determine number of devices on chain
**
** Input Parameters: 
** pProslic: pointer to PROSLIC object
**
** Return:
** number of channels detected
*/
static uInt8 probeDaisyChain (proslicChanType *pProslic){
	uInt8 i=0;
	WriteReg(pProHW,BROADCAST,RAM_ADDR_HI,0x40);
	while (ReadReg(pProHW,i++,RAM_ADDR_HI) == 0x40 && (i<=32));
	WriteReg(pProHW,BROADCAST,RAM_ADDR_HI,0x0);
	return i-1;
}

/*
** Function: cal_iteration
**
** Description: 
** Calibrate channels from different devices in parallel
**
** Input Parameters: 
** pProslic: pointer to PROSLIC object
** iter: channel
** data: pointer to array of cal enable bits
** numChan: total number of channels in chain
**
** Return:
** error
*/

static int cal_iteration (proslicChanType *pProslic,uInt8 iter,uInt8 *data2,uInt8 numChan){
	uInt8 i;
	uInt8 data,data3;
	int millisecs=0;
	for (i=0;i<numChan;i+=2){ /*we do each channel in each chip*/
		WriteReg(pProHW,i+iter,CALR0,data2[0]);
		WriteReg(pProHW,i+iter,CALR1,data2[1]);
		WriteReg(pProHW,i+iter,CALR2,data2[2]);
		WriteReg(pProHW,i+iter,CALR3,data2[3]);
	}
	do{
		data3=0;
		for (i=0;i<numChan;i+=2){
			data = ReadReg(pProHW,i+iter,CALR3);
			data3 |= data;
		}
		Delay(pProTimer,1);
		if (millisecs++ > 3000){
#ifdef ENABLE_DEBUG
			if (pProslic->debugMode)
				LOGPRINT("%sCalibration timeout channel %d\n", LOGPRINT_PREFIX, iter);
#endif
		}
	}while ((data3&0x80)&&(millisecs<3000));
	return data3;
}


/*
** Function: Si3226_Calibrate
**
** Description: 
** Performs calibration based on passed ptr to array of
** desired CALRn settings.
**
** Run serial calibration.  Return error code on timeout
**
*/
int Si3226_Calibrate(proslicChanType_ptr *pProslic, int maxChan, uInt8 *calr, int maxTime)
{
int i,j;
int cal_en = 0;
int cal_en_chan = 0;
int timer = 0;

    /*
    ** Launch calibration on 1st port of each device in parallel, then
    ** launch on 2nd port of each device. 
    */
    for(j=0;j<SI322X_CHAN_PER_DEVICE;j++)
    {
        for(i=j;i<maxChan;i+=SI322X_CHAN_PER_DEVICE)
        {
            if(pProslic[i]->channelEnable)
            {
                pProslic[i]->WriteRegX(pProslic[i]->pProHWX,pProslic[i]->channel,CALR0,calr[0]);
                pProslic[i]->WriteRegX(pProslic[i]->pProHWX,pProslic[i]->channel,CALR1,calr[1]);
                pProslic[i]->WriteRegX(pProslic[i]->pProHWX,pProslic[i]->channel,CALR2,calr[2]);
                pProslic[i]->WriteRegX(pProslic[i]->pProHWX,pProslic[i]->channel,CALR3,calr[3]);
            }
        }

        timer = 0;
        do 
        {
            cal_en = 0;
            pProslic[0]->DelayX(pProslic[0]->pProTimerX,10);
            for(i=j;i<maxChan;i+=SI322X_CHAN_PER_DEVICE)
            {
                if(pProslic[i]->channelEnable)
                {
                    cal_en_chan = pProslic[i]->ReadRegX(pProslic[i]->pProHWX,pProslic[i]->channel,CALR3);
                    if((cal_en_chan&0x80)&&(timer == maxTime))
                    {
    #ifdef ENABLE_DEBUG
                        if(pProslic[i]->debugMode)
                        {
                            LOGPRINT("%sCalibration timout channel %d\n", LOGPRINT_PREFIX, i);
                        }
    #endif
                        pProslic[i]->channelEnable = 0;
                        pProslic[i]->error = RC_CAL_TIMEOUT;
                    }
                    cal_en |= cal_en_chan;
                }
            }         
        }while((timer++ <= maxTime)&&(cal_en&0x80));
    }

    return cal_en;
}
/*
** Function: Si3226_PowerUpConverter
**
** Description: 
** Powers up DC/DC converter
**
** Returns:
** int (error)
**
*/
int Si3226_PowerUpConverter(proslicChanType_ptr pProslic)
{
errorCodeType error = RC_NONE;
int32 vbath,vbat;
int timer = 0;
    /*
    ** Steps 1-5:
    ** - powerup dc/dc w/ OV clamping and shutdown
    ** - delay
    ** - clear dcdc status
    ** - switch to OV clamping only (no shutdown)
    ** - delay
    */
    
    ProSLIC_SetUserMode(pProslic,TRUE, FALSE);
    WriteRAM(pProHW,pProslic->channel,PD_DCDC,0x700000L);
    WriteRAM(pProHW,pProslic->channel,PD_DCDC,0x600000L);
    Delay(pProTimer,50);
    WriteRAM(pProHW,pProslic->channel,DCDC_STATUS,0L);   
    WriteRAM(pProHW,pProslic->channel,PD_DCDC,0x400000L);
    Delay(pProTimer,50);

    /*
    ** Step 6:
    ** - monitor vbat vs expected level (VBATH_EXPECT)
    */
    vbath = ReadRAM(pProHW,pProslic->channel,VBATH_EXPECT);
    do
    {
	    vbat = ReadRAM(pProHW,pProslic->channel,MADC_VBAT);
        if(vbat & 0x10000000L)
            vbat |= 0xF0000000L;
		Delay(pProTimer,10);
    }while((vbat < (vbath - COMP_5V))&&(timer++ < SI3226_TIMEOUT_DCDC_UP));  /* 2 sec timeout */

#ifdef ENABLE_DEBUG
        if(pProslic->debugMode)
        {
            LOGPRINT ("%sVBAT Up = %d.%d v\n",
                LOGPRINT_PREFIX,
                (int)((vbat/SCALE_V_MADC)/1000), (int)(((vbat/SCALE_V_MADC) - (vbat/SCALE_V_MADC)/1000*1000)));
        }
#endif
    if(timer > SI3226_TIMEOUT_DCDC_UP)
    {
        /* Error handling - shutdown converter, disable channel, set error tag */
        pProslic->channelEnable = 0;
        error = RC_VBAT_UP_TIMEOUT;
        WriteRAM(pProHW,pProslic->channel,PD_DCDC, 0x900000L); /* shutdown converter */
#ifdef ENABLE_DEBUG
        if(pProslic->debugMode)
        {
	        LOGPRINT ("%sDCDC Power up timeout channel %d - disabling channel\n",
                LOGPRINT_PREFIX, pProslic->channel);
        }
#endif      
    }

    return error;  
}

/*
** Function: Si3226_PowerDownConverter
**
** Description: 
** Safely powerdown dcdc converter after ensuring linefeed
** is in the open state.  Test powerdown by setting error
** flag if detected voltage does no fall below 5v.
**
** Returns:
** int (error)
**
*/
int Si3226_PowerDownConverter(proslicChanType_ptr pProslic)
{
errorCodeType error = RC_NONE;
ramData vbat;
int timer = 0;
    
    WriteReg(pProHW,pProslic->channel,LINEFEED, LF_OPEN);
    Delay(pProTimer,50);
    WriteRAM(pProHW,pProslic->channel,PD_DCDC,0x300000L);
    Delay(pProTimer,50);

    /*
    ** Verify VBAT falls below 10v (QCUK slower because
    ** because of larger output cap)
    */
    do
    {
	    vbat = ReadRAM(pProHW,pProslic->channel,MADC_VBAT);
		Delay(pProTimer,10);
    }while((vbat > COMP_10V)&&(timer++ < SI3226_TIMEOUT_DCDC_DOWN));  /* 2sec timeout */
#ifdef ENABLE_DEBUG
    if(pProslic->debugMode)
    {
        LOGPRINT ("%sVBAT Down = %d.%d v after %d ms\n", LOGPRINT_PREFIX,
            (int)((vbat/SCALE_V_MADC)/1000), 
            (int)(((vbat/SCALE_V_MADC) - (vbat/SCALE_V_MADC)/1000*1000)),timer*10);
    }
#endif
    if(timer > SI3226_TIMEOUT_DCDC_DOWN)
    {
        /* Error handling - shutdown converter, disable channel, set error tag */
        pProslic->channelEnable = 0;
        error = RC_VBAT_DOWN_TIMEOUT;
#ifdef ENABLE_DEBUG
        if(pProslic->debugMode)
        {
	        LOGPRINT ("%sDCDC Power Down timeout channel %d\n", LOGPRINT_PREFIX, pProslic->channel);
        }
#endif      
    }

    return error;  
}


/*
** Function: calibrate
**
** Description: 
** Performs calibration (use deprecated) - replaced by Si3226_Calibrate()
**
*/

static int calibrate (proslicChanType *pProslic, int broadcast){
	/*
	** This function will perform the ProSLIC calibration sequence 
	*/
	int k;
    uInt8 i,j;
	uInt8 data [] = {0x0, 0x0, 0x1, 0x80};/*madc cal is done before other cals*/
	uInt8 data3=0;
	uInt8 numChan=0;
	ramData vbat,vbat_min=0,vbath;
	ProSLIC_SetUserMode(pProslic,TRUE, broadcast);
	if (broadcast){
		/*# fix coefficient for zcal*/
		WriteRAM(pProHW,BROADCAST,746,0x8F00000L);
		WriteRAM(pProHW,BROADCAST,927,0x1FFE0000L);
		WriteRAM(pProHW,BROADCAST,1537,0x3200000L); /*fix lkg stndby offset for oht*/
		numChan = probeDaisyChain(pProslic);
		for (i=0;i<2;i++){
			data3 |= cal_iteration(pProslic,i,data,numChan);
		}
	}
	else {/*to do - add parallel cals on separate devices*/
		/*# fix coefficient for zcal*/
		WriteRAM(pProHW,pProslic->channel,746,0x8F00000L);
		WriteRAM(pProHW,pProslic->channel,927,0x1FFE0000L);
		WriteRAM(pProHW,pProslic->channel,1537,0x3200000L); /*fix lkg stndby offset for oht*/
		numChan = 1;
		i=0;
		WriteReg(pProHW,pProslic->channel,CALR0,data[0]);
		WriteReg(pProHW,pProslic->channel,CALR1,data[1]);
		WriteReg(pProHW,pProslic->channel,CALR2,data[2]);
		WriteReg(pProHW,pProslic->channel,CALR3,data[3]);
		do{
			data3 = ReadReg(pProHW,pProslic->channel,CALR3);
			Delay(pProTimer,1);

			if (i++ > 3000){
#ifdef ENABLE_DEBUG
				if (pProslic->debugMode)
					LOGPRINT("%sCalibration timeout channel %d\n", LOGPRINT_PREFIX, pProslic->channel);
#endif
				pProslic->error = RC_CAL_TIMEOUT;
				pProslic->channelEnable = 0;
			
			}
		} while (i<3000 && (data3&0x80));
	}
#ifdef ENABLE_DEBUG
	if (pProslic->debugMode)
		LOGPRINT("%sMADC cal done. channel %d\n", LOGPRINT_PREFIX, pProslic->channel);
#endif

	/*run the rest of the cals*/
	data[1] = 0xbf; /*no zcal*/
	data[2] = 0xf8;
	data[3] = 0x80;
	ProSLIC_SetUserMode(pProslic,TRUE, broadcast);
	if (broadcast){
		
		for (i=0;i<2;i++){
			/*start up convertor*/
			
			WriteRAM(pProHW,BROADCAST,1538, 0x600000L);
			Delay(pProTimer,100);
			WriteRAM(pProHW,BROADCAST,1538, 0x400000L);
			
			k=0;
			do {
				vbat_min = 0x7FFFFFFFL;
				vbath = ReadRAM(pProHW,i,VBATH_EXPECT);
				for (j=0;j<numChan;j+=2){
					vbat = ReadRAM(pProHW,j+i,MADC_VBAT);
					if (vbat < vbat_min)
						vbat_min = vbat;
				}
				Delay(pProTimer,10);
				if (k > 200){
#ifdef ENABLE_DEBUG
					LOGPRINT ("%sDC-DC Power up timeout\n", LOGPRINT_PREFIX);
#endif
					return RC_VBAT_UP_TIMEOUT;
				}
			} while ((vbat_min < (vbath-0x51EB82L)) && (k++ < 200)); /*2 second timeout*/
			data3 |= cal_iteration(pProslic,i,data,numChan);
		}
	}
	else {
		
		/*converter startup sequence*/
		WriteRAM(pProHW,pProslic->channel,1538, 0x600000L);
		Delay(pProTimer,100);
		WriteRAM(pProHW,pProslic->channel,1538, 0x400000L); 
		
		k=0;
		do {
				vbath = ReadRAM(pProHW,pProslic->channel,VBATH_EXPECT);
				vbat = ReadRAM(pProHW,pProslic->channel,MADC_VBAT);			
				Delay(pProTimer,10);
				if (k > 200){
#ifdef ENABLE_DEBUG
					LOGPRINT ("%sDC-DC Power up timeout\n", LOGPRINT_PREFIX);
#endif
					return RC_VBAT_UP_TIMEOUT;
				}
		} while ((vbat < (vbath-0x51EB82L)) && (k++ < 200)); /*2 second timeout, 5V margin*/
		i=0;
		WriteReg(pProHW,pProslic->channel,CALR0,data[0]);
		WriteReg(pProHW,pProslic->channel,CALR1,data[1]);
		WriteReg(pProHW,pProslic->channel,CALR2,data[2]);
		WriteReg(pProHW,pProslic->channel,CALR3,data[3]);
		do{
			data3 = ReadReg(pProHW,pProslic->channel,CALR3);
			Delay(pProTimer,100);

			if (i++ > 30){
#ifdef ENABLE_DEBUG
				if (pProslic->debugMode)
					LOGPRINT("%sCalibration timeout channel %d\n", LOGPRINT_PREFIX, pProslic->channel);
#endif
				pProslic->error = RC_CAL_TIMEOUT;
				pProslic->channelEnable = 0;
			
			}
		} while (i<30 && (data3&0x80));
	}
#ifdef ENABLE_DEBUG
	if (pProslic->debugMode)
		LOGPRINT("%sCalibration done. channel %d\n", LOGPRINT_PREFIX, pProslic->channel);
#endif
	return data3;
}

/*
** Function: enableSi3226Loopman
**
** Description: 
** Turns of ISNS HVIC mode and enables MADC_LOOPMAN
** for current sensing 
*/

#ifdef DISABLE_LOOPMAN
#else
int enableSi3226Loopman (proslicChanType *pProslic, int broadcast){
	uInt8 channel;
		ProSLIC_SetUserMode (pProslic,TRUE, broadcast); /*make sure we are in user mode */
	if (broadcast){
		channel = BROADCAST;
	}
	else{
		channel = pProslic->channel;
	}

    /* Removed forced standby disable - fixed on revC */

    /* enable loopman */
	WriteRAM(pProHW, channel,MADC_LOOP_MAN, 0x300000L);
	WriteReg(pProHW, channel,PDN,0x80); /*enable MADC to prevent power alarm. this could also be done before going active*/
	
	return 0;
}

/*
** Function: disableSi3226Loopman
**
** Description: 
** Turns off MADC_LOOPMAN and enables HVIC ISNS mode for
** current sensing
*/
int disableSi3226Loopman (proslicChanType *pProslic, int broadcast){
	uInt8 channel;
	ProSLIC_SetUserMode(pProslic,TRUE, broadcast);
	if (broadcast){
		channel = BROADCAST;
	}
	else{
		channel = pProslic->channel;
	}
	WriteRAM(pProHW, channel,HVIC_CNTL_MAN, 0x200000L);
	WriteRAM(pProHW, channel,MADC_LOOP_MAN, 0x200000L);
	
	return RC_NONE;
}
#endif


/*
** Functions below are defined in header file and can be called by external files
*/

/*
**
** PROSLIC INITIALIZATION FUNCTIONS
**
*/

/*
** Function: ProSLIC_ShutdownChannel
**
** Description: 
** Safely shutdown channel w/o interruptions to
** other active channels
**
** Input Parameters: 
** pProslic: pointer to PROSLIC channel object
**
** Return:
** 0

*/
int Si3226_ShutdownChannel (proslicChanType_ptr pProslic){
	/*
	** set linefeed to open state, powerdown dcdc converter
	*/
    Si3226_SetLinefeedStatus(pProslic,LF_OPEN);
    Delay(pProTimer,10);
    Si3226_PowerDownConverter(pProslic);
    
	return 0;
}

/*
** Function: Si3226_VerifyControlInterface
**
** Description: 
** Check control interface read back capabilities
**
** Input Parameters: 
** pProslic: pointer to PROSLIC channel object
**
** Return:
** 0
*/
int Si3226_VerifyControlInterface (proslicChanType_ptr pProslic)
{
	uInt8 i;
	int numOfChan = probeDaisyChain(pProslic);
	if (numOfChan == 0)
		return RC_SPI_FAIL;
#ifdef ENABLE_DEBUG
	if (pProslic->debugMode)
		LOGPRINT ("%sFound %d channels\n", LOGPRINT_PREFIX, numOfChan);
#endif
	WriteReg(pProHW,BROADCAST,PCMRXLO,0x5a);
	WriteRAM(pProHW,BROADCAST,UNUSED449,0x12345678L);

	for (i=0;i<numOfChan;i++){
		/*Try to write innocuous register to test SPI is working*/
		
		if (ReadReg(pProHW,i,PCMRXLO) != 0x5A){
			PROSLIC_PRINT_ERROR(pProslic,RC_SPI_FAIL);
#ifdef ENABLE_DEBUG
			if (pProslic->debugMode)
				LOGPRINT("%sProslic %d not communicating. Register access fail.\n", LOGPRINT_PREFIX, i);
#endif
			return RC_SPI_FAIL;
		}	
		if (ReadRAM(pProHW,i,UNUSED449) != 0x12345678L){
			PROSLIC_PRINT_ERROR(pProslic,RC_SPI_FAIL);
#ifdef ENABLE_DEBUG
			if (pProslic->debugMode)
				LOGPRINT("%sProslic %d not communicating. RAM access fail.\n", LOGPRINT_PREFIX, i);
#endif
			return RC_SPI_FAIL;
		}

	}
	return 0;
}

/*
** Function: getChipType
**
** Description: 
** Decode ID register to identify chip type
**
** Input Parameters: 
** ID register value
**
** Return:
** partNumberType
*/
static partNumberType getChipType(uInt8 data){
    data &= 0x38;
    return ((data >> 3) + SI3226);
}

/*
** Function: identifyChannelType
**
** Description: 
** Determine if DAA or ProSLIC present
**
** Input Parameters: 
** pProslic: pointer to PROSLIC channel object
**
** Return:
** channelType
*/
static int identifyChannelType(proslicChanType *pProslic){
uInt8 data;
    /*
    **  Register 13 (DAA) always has bits 0:1 set to 0 and bit 6 set to 1
    **  Register 13 (PROSLIC) can have bits 0:1, and 4 set, while all others are undefined
    **  Write 0x13 to Reg 13. The following return values are expected -
    **
    **   0x00 or 0xFF    :    No device present
    **   0x4X            :    DAA
    **   0x13            :    PROSLIC
    */
    WriteReg(pProHW,pProslic->channel,PCMTXHI,0x13);
    Delay(pProTimer,5);

    /* Now check if the value took */
    data = ReadReg(pProHW,pProslic->channel,PCMTXHI); 
    if( data == 0x13)
       return PROSLIC;
    else
       return UNKNOWN;
}

/*
** Function: Si3226_Init_MultiBOM
**
** Description: 
** - probe SPI to establish daisy chain length
** - load patch
** - initialize general parameters
** - calibrate madc
** - bring up DC/DC converters
** - calibrate everything except madc & lb
**
** Input Parameters: 
** pProslic: pointer to PROSLIC object array
** fault: error code
**
** Return:
** error code
*/


#ifdef SIVOICE_MULTI_BOM_SUPPORT
int Si3226_Init_MultiBOM(proslicChanType_ptr *pProslic,int size,int preset) {

	if(preset < si3226_genconf_multi_max_preset)
	{
		/* Copy selected General Configuration parameters to std structure */
		Si3226_General_Configuration = Si3226_General_Configuration_MultiBOM[preset];
	}
	else
	{
		return RC_INVALID_PRESET;
	}
	return Si3226_Init(pProslic,size);
}
#endif


/*
** Function: Si3226_Init_with_Options
**
** Description: 
** - probe SPI to establish daisy chain length
** - load patch
** - initialize general parameters
** - calibrate madc
** - bring up DC/DC converters
** - calibrate everything except madc & lb
**
** Input Parameters: 
** pProslic: pointer to PROSLIC object array
** fault: error code
**
** Return:
** error code
*/
/* TODO: Generic patch label (prevent unnecessary sw revs)
   TODO: broadcast gen param load and patch load (save ex time)
   TODO: patch verification failure error handling
   TODO: dcdc powerup error handling
   TODO: calibration timeout error handling
*/
int Si3226_Init_with_Options (proslicChanType_ptr *pProslic, int size, initOptionsType init_opt){
	/*
	** This function will initialize the chipRev and chipType members in pProslic
	** as well as load the initialization structures.
	*/
	uInt8 data; 
    uInt8 calSetup[] = {0x00, 0x00, 0x01, 0x80}; /* CALR0 - CALR3 */
	int k;
    const proslicPatch *patch;

	if(init_opt != INIT_REINIT)
	{
		/*
		** Identify channel type (ProSLIC or DAA) before initialization.
		** Channels identified as DAA channels will not be modified during
		** the ProSLIC initialization
		*/
		for (k=0;k<size;k++)
		{
			pProslic[k]->channelType = identifyChannelType(pProslic[k]);
#ifdef ENABLE_DEBUG
			if(pProslic[k]->debugMode) 
			{
        const char fmt_string[] = "%s Channel %d: Type = %s\n";
        char       *type_string;

				if(pProslic[k]->channelType == PROSLIC)
        {
          type_string = "PROSLIC";
        }
				else if(pProslic[k]->channelType == DAA)
        {
          type_string = "DAA";
        }
				else
        {
          type_string = "UNKNOWN";
        }            
				LOGPRINT(fmt_string, LOGPRINT_PREFIX, pProslic[k]->channel, type_string);
			}
#endif
		}

		/*
		** Read channel id to establish chipRev and chipType
		*/
		for (k=0;k<size;k++)
		{
			if(pProslic[k]->channelType == PROSLIC)
			{
				data = pProslic[k]->ReadRegX(pProslic[k]->pProHWX,pProslic[k]->channel,ID);
				pProslic[k]->deviceId->chipRev = data&0x7;
				pProslic[k]->deviceId->chipType = getChipType(data);
			}
		}


		/*
		** Probe each channel and enable all channels that respond 
		*/
		for (k=0;k<size;k++)
		{
			if((pProslic[k]->channelEnable)&&(pProslic[k]->channelType == PROSLIC))
			{
				pProslic[k]->WriteRegX(pProslic[k]->pProHWX,pProslic[k]->channel,PCMRXLO,0x5a);
				if (pProslic[k]->ReadRegX(pProslic[k]->pProHWX,pProslic[k]->channel,PCMRXLO) != 0x5A)
				{
					pProslic[k]->channelEnable = 0;
					pProslic[k]->error = RC_SPI_FAIL;
					return RC_SPI_FAIL;
				}
			}
		}

	}/* init_opt */
	
	if((init_opt != INIT_REINIT)&&(init_opt != INIT_NO_PATCH_LOAD))
	{
		/*
		** Load patch (do not enable until patch loaded on all channels)
		*/
		for (k=0;k<size;k++)
		{
			if ((pProslic[k]->channelEnable)&&(pProslic[k]->channelType == PROSLIC))
			{
				switch(pProslic[k]->deviceId->chipRev) 
				{
					case C:
#ifdef SIVOICE_MULTI_BOM_SUPPORT
						if(Si3226_General_Configuration.bomOpt == BO_DCDC_FLYBACK)
						{
							patch = &(SI3226_PATCH_C_FLBK);
						}
						else if(Si3226_General_Configuration.bomOpt == BO_DCDC_QCUK)
						{
							patch = &(SI3226_PATCH_C_QCUK);
						}
						else
						{
#ifdef ENABLE_DEBUG
							if(pProslic[k]->debugMode)
							{
								LOGPRINT("%sChannel %d : Invalid Patch\n",
                                    LOGPRINT_PREFIX, pProslic[k]->channel);
							}
#endif
							pProslic[k]->channelEnable = 0;
							pProslic[k]->error = RC_INVALID_PATCH;
							return RC_INVALID_PATCH;
						}
#else
						patch = &(SI3226_PATCH_C_DEFAULT);
#endif
						break;
					case D:
#ifdef SIVOICE_MULTI_BOM_SUPPORT
						if(Si3226_General_Configuration.bomOpt == BO_DCDC_FLYBACK)
						{
							patch = &(SI3226_PATCH_D_FLBK);
						}
						else if(Si3226_General_Configuration.bomOpt == BO_DCDC_QCUK)
						{
							patch = &(SI3226_PATCH_D_QCUK);
						}
						else
						{
#ifdef ENABLE_DEBUG
							if(pProslic[k]->debugMode)
							{
								LOGPRINT("%sChannel %d : Invalid Patch\n", 
                                    LOGPRINT_PREFIX, pProslic[k]->channel);
							}
#endif
							pProslic[k]->channelEnable = 0;
							pProslic[k]->error = RC_INVALID_PATCH;
							return RC_INVALID_PATCH;
						}
#else
						patch = &(SI3226_PATCH_D_DEFAULT);
#endif
						break;
					case E:
#ifdef SIVOICE_MULTI_BOM_SUPPORT
						if(Si3226_General_Configuration.bomOpt == BO_DCDC_FLYBACK)
						{
							patch = &(SI3226_PATCH_E_FLBK);
						}
						else
						{
#ifdef ENABLE_DEBUG
							if(pProslic[k]->debugMode)
							{
								LOGPRINT("%sChannel %d : Invalid Patch\n", 
                                    LOGPRINT_PREFIX, pProslic[k]->channel);
							}
#endif
							pProslic[k]->channelEnable = 0;
							pProslic[k]->error = RC_INVALID_PATCH;
							return RC_INVALID_PATCH;
						}
#else
						patch = &(SI3226_PATCH_E_DEFAULT);
#endif
						break;
					default:
#ifdef ENABLE_DEBUG
						if (pProslic[k]->debugMode)
						{
							LOGPRINT("%sChannel %d : Unsupported Device Revision (%d)\n",
                                LOGPRINT_PREFIX, pProslic[k]->channel,pProslic[k]->deviceId->chipRev );
						}
#endif
						pProslic[k]->channelEnable = 0;
						pProslic[k]->error = RC_UNSUPPORTED_DEVICE_REV;
						return RC_UNSUPPORTED_DEVICE_REV;
				}/* switch */

				Si3226_LoadPatch(pProslic[k],patch);

#ifdef DISABLE_VERIFY_PATCH
#else
				if (Si3226_VerifyPatch(pProslic[k],patch)){
					pProslic[k]->channelEnable=0;
					pProslic[k]->error = RC_PATCH_ERR;	
					return RC_PATCH_ERR;
				} else {
					pProslic[k]->WriteRegX (pProslic[k]->pProHWX, pProslic[k]->channel,JMPEN,1);  
				}
#endif				
			}
		}
	}/* init_opt */

    /*
	** Load general parameters - includes all BOM dependencies.
    ** Any differences in settings among revisions is handled 
    ** in preset.
    */
	for (k=0;k<size;k++){ 
		if (pProslic[k]->channelEnable){
	
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_VREF_MIN,Si3226_General_Configuration.dcdc_vref_min);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_VREF_MIN_RNG,Si3226_General_Configuration.dcdc_vref_min_ring);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,VBATR_EXPECT,Si3226_General_Configuration.vbatr_expect);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_FSW_NORM,Si3226_General_Configuration.dcdc_fsw_norm);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_FSW_NORM_LO,Si3226_General_Configuration.dcdc_fsw_norm_lo);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_DIN_LIM,Si3226_General_Configuration.dcdc_din_lim);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_FSW_RING,Si3226_General_Configuration.dcdc_fsw_ring);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_FSW_RING_LO,Si3226_General_Configuration.dcdc_fsw_ring_lo);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_OITHRESH,Si3226_General_Configuration.dcdc_oithresh);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_OVTHRESH,Si3226_General_Configuration.dcdc_ovthresh);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_UVHYST,Si3226_General_Configuration.dcdc_uvhyst);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_UVTHRESH,Si3226_General_Configuration.dcdc_uvthresh);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_VOUT_LIM,Si3226_General_Configuration.dcdc_vout_lim);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_FSW_VHYST,Si3226_General_Configuration.dcdc_fsw_vhyst);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_FSW_VTHLO,Si3226_General_Configuration.dcdc_fsw_vthlo);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_SWDRV_POL,Si3226_General_Configuration.dcdc_swdrv_pol);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_SWFET,Si3226_General_Configuration.dcdc_swfet);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_VREF_CTRL,Si3226_General_Configuration.dcdc_vref_ctrl);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,DCDC_RNGTYPE,Si3226_General_Configuration.dcdc_rngtype);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,COEF_P_HVIC,Si3226_General_Configuration.coef_p_hvic);
			pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,P_TH_HVIC,Si3226_General_Configuration.p_th_hvic);
            pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,AUTO,Si3226_General_Configuration.autoRegister); 

            /* Hardcoded mods to default settings - applies to all rev's and configs */
            data = pProslic[k]->ReadRegX(pProslic[k]->pProHWX, pProslic[k]->channel,GPIO_CFG1);
            data &= 0xF9;
            data |= 0x60;
            pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,GPIO_CFG1,data); /* coarse sensors analog mode */
            pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,PDN,0x80); /* madc powered in open state */
            pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,TXACHPF_A1_1,0x71EB851L); /* Fix HPF corner */
            pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,ROW0_C2, 0x723F235L);   /* improved DTMF det */
            pProslic[k]->WriteRAMX(pProslic[k]->pProHWX, pProslic[k]->channel,ROW1_C2, 0x57A9804L);   /* improved DTMF det */
		}
	}

	if((init_opt != INIT_NO_CAL)&&(init_opt != INIT_REINIT))
	{
		/*
		** Calibrate (madc offset)
		*/
		Si3226_Calibrate(pProslic,size,calSetup,TIMEOUT_MADC_CAL);
	}
    /*
	** Bring up DC/DC converters sequentially to minimize
    ** peak power demand on VDC
    */
	for (k=0;k<size;k++)
    { 
		if (pProslic[k]->channelEnable)
        {
            pProslic[k]->error = Si3226_PowerUpConverter(pProslic[k]);
        }
    }

	if((init_opt != INIT_NO_CAL)&&(init_opt != INIT_REINIT))
	{
		/*
		** Execute remaining cals (except madc, lb)
		*/
		calSetup[1] = SI3226_CAL_STD_CALR1;
		calSetup[2] = SI3226_CAL_STD_CALR2;
		Si3226_Calibrate(pProslic,size,calSetup,TIMEOUT_GEN_CAL);
	}
    /*
    ** Enable Loopman
    */
	for (k=0;k<size;k++){ /*chan 0 shut off during chan 1 bring up*/
		if (pProslic[k]->channelEnable){
#ifdef DISABLE_LOOPMAN
#else
				enableSi3226Loopman(pProslic[k],FALSE);
#endif
		}
	}

	return 0;
}

/*
** Function: Si3226_Reinit
**
** Description: 
** Performs soft reset then calls Si3226x_Init
**
** Input Parameters: 
** pProslic: pointer to PROSLIC object array
** fault: error code
**
** Return:
** error code
*/
int Si3226_Reinit (proslicChanType_ptr pProslic, int size){
    uInt8 lf;
    int retVal;
    int num_reinit_chan = 1;
    SILABS_UNREFERENCED_PARAMETER(size);

	lf = ReadReg(pProHW,pProslic->channel,LINEFEED);
	Si3226_PowerDownConverter(pProslic);
	Delay(pProTimer,10);
	/* Determine which soft reset to assert (dual device) */
    if((pProslic->channel % 2) == 0) /* Even */
	{
	    WriteReg(pProHW,pProslic->channel,RESET,0x01); /* device ch 0 */
	}
	else
	{
	    WriteReg(pProHW,pProslic->channel,RESET,0x02); /* device ch 1 */
	}
	Delay(pProTimer,100);

	retVal = Si3226_Init_with_Options(&pProslic,num_reinit_chan,INIT_REINIT);
	/* 
	** Restore entry linefeed state - if alarm occurred, this
	** is likely to be the OPEN state, but this function should
	** be useful for all usage cases.
	*/
	WriteReg(pProHW,pProslic->channel,LINEFEED,lf); 
	return (retVal);
}

/*
** Function: Si3226_LBCal
**
** Description: 
** Sequential canned longitudinal balance calibration. 
**
** Input Parameters: 
** pProslic: pointer to array of PROSLIC channel objects
** size:     number of PROSLIC channel objects   
**
** Return:
** 0
*/
int Si3226_LBCal (proslicChanType_ptr *pProslic, int size)
{
	int k,i=0;
	uInt8 lf; uInt8 data;

	for (k=0;k<size;k++){
		if (pProslic[k]->channelEnable){
			lf = pProslic[k]->ReadRegX(pProslic[k]->pProHWX, pProslic[k]->channel,LINEFEED); 
			Si3226_SetLinefeedStatus(pProslic[k],LF_OPEN);
#ifdef DISABLE_LOOPMAN
#else
			disableSi3226Loopman(pProslic[k],FALSE);
#endif
#ifdef ENABLE_DEBUG
        if(pProslic[k]->debugMode)
        {
			LOGPRINT("%sStarting LB Cal on channel %d\n", LOGPRINT_PREFIX, pProslic[k]->channel);
        }
#endif

			Si3226_SetLinefeedStatus(pProslic[k],LF_FWD_ACTIVE);
			
			pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,CALR0,CAL_LB_ALL); /*enable LB cal*/
			pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,CALR3,0x80); /*start cal*/
			i=0;
			do {
				data = pProslic[k]->ReadRegX(pProslic[k]->pProHWX,pProslic[k]->channel,CALR3);
				pProslic[k]->DelayX(pProslic[k]->pProTimerX,10);
				if (i++ >= TIMEOUT_LB_CAL){
#ifdef ENABLE_DEBUG
				if (pProslic[k]->debugMode)
					LOGPRINT("%sCalibration timeout channel %d\n", LOGPRINT_PREFIX, pProslic[k]->channel);
#endif
				pProslic[k]->error = RC_CAL_TIMEOUT;
				pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,LINEFEED,LF_OPEN); 
				return RC_CAL_TIMEOUT;
			
			}
			} while (data&0x80 && i<=TIMEOUT_LB_CAL);
#ifdef DISABLE_LOOPMAN
#else
			enableSi3226Loopman(pProslic[k],FALSE);
#endif
			pProslic[k]->WriteRegX(pProslic[k]->pProHWX, pProslic[k]->channel,LINEFEED,lf); 
	
		}
	}
	return 0;
}

int Si3226_SetLinefeedStatusBroadcast (proslicChanType_ptr pProslic, uInt8 newLinefeed){
	WriteReg(pProHW,BROADCAST,LINEFEED,newLinefeed);
	return 0;
}

/*
** Function: PROSLIC_Cal
**
** Description: 
** Calibrates the ProSLIC
*/
int Si3226_Cal (proslicChanType_ptr *pProslic, int size){
	/*
	** This function will perform the ProSLIC calibration sequence (for all channels)
	*/
	int i,j=0;
	for (i=0;i<size;i++){
		if (pProslic[i]->channelEnable){
#ifdef DISABLE_LOOPMAN
#else
			/* disableSi3226Loopman(pProslic[i],FALSE); */
#endif
			j |= calibrate(pProslic[i],0);
#ifdef DISABLE_LOOPMAN
#else
			/* enableSi3226Loopman(pProslic[i],FALSE); */
#endif
		}
	}
	return j;
}

/*
** Function: PROSLIC_EnableInterrupts
**
** Description: 
** Enables interrupts
*/
int Si3226_EnableInterrupts (proslicChanType_ptr pProslic){
	WriteReg (pProHW,pProslic->channel,IRQEN1,Si3226_General_Configuration.irqen1);
	WriteReg (pProHW,pProslic->channel,IRQEN2,Si3226_General_Configuration.irqen2);
	WriteReg (pProHW,pProslic->channel,IRQEN3,Si3226_General_Configuration.irqen3);
	WriteReg (pProHW,pProslic->channel,IRQEN4,Si3226_General_Configuration.irqen4);
	return 0;
}

/*
**
** PROSLIC CONFIGURATION FUNCTIONS
**
** These functions configure the ProSLIC
** referencing extern configuration structures
** generated by the ProSLIC API Config Tool
**
*/

/*
** Function: PROSLIC_RingSetup
**
** Description: 
** configure ringing
*/
#ifndef DISABLE_RING_SETUP
int Si3226_RingSetup (proslicChanType *pProslic, int preset){

    WriteRAM(pProHW,pProslic->channel,RTPER,Si3226_Ring_Presets[preset].rtper);
    WriteRAM(pProHW,pProslic->channel,RINGFR,Si3226_Ring_Presets[preset].freq);
    WriteRAM(pProHW,pProslic->channel,RINGAMP,Si3226_Ring_Presets[preset].amp);
    WriteRAM(pProHW,pProslic->channel,RINGPHAS,Si3226_Ring_Presets[preset].phas);
    WriteRAM(pProHW,pProslic->channel,RINGOF,Si3226_Ring_Presets[preset].offset);
    WriteRAM(pProHW,pProslic->channel,SLOPE_RING,Si3226_Ring_Presets[preset].slope_ring);
    WriteRAM(pProHW,pProslic->channel,IRING_LIM,Si3226_Ring_Presets[preset].iring_lim);
    WriteRAM(pProHW,pProslic->channel,RTACTH,Si3226_Ring_Presets[preset].rtacth);
    WriteRAM(pProHW,pProslic->channel,RTDCTH,Si3226_Ring_Presets[preset].rtdcth);
    WriteRAM(pProHW,pProslic->channel,RTACDB,Si3226_Ring_Presets[preset].rtacdb);
    WriteRAM(pProHW,pProslic->channel,RTDCDB,Si3226_Ring_Presets[preset].rtdcdb);
    WriteRAM(pProHW,pProslic->channel,VOV_RING_BAT,Si3226_Ring_Presets[preset].vov_ring_bat);
    WriteRAM(pProHW,pProslic->channel,VOV_RING_GND,Si3226_Ring_Presets[preset].vov_ring_gnd);
    WriteRAM(pProHW,pProslic->channel,VBATR_EXPECT,Si3226_Ring_Presets[preset].vbatr_expect);
    WriteReg(pProHW,pProslic->channel,RINGTALO,Si3226_Ring_Presets[preset].talo);
    WriteReg(pProHW,pProslic->channel,RINGTAHI,Si3226_Ring_Presets[preset].tahi);
    WriteReg(pProHW,pProslic->channel,RINGTILO,Si3226_Ring_Presets[preset].tilo);
    WriteReg(pProHW,pProslic->channel,RINGTIHI,Si3226_Ring_Presets[preset].tihi);
  
    WriteRAM(pProHW,pProslic->channel,DCDC_VREF_MIN_RNG,Si3226_Ring_Presets[preset].dcdc_vref_min_rng);
    WriteReg(pProHW,pProslic->channel,RINGCON,Si3226_Ring_Presets[preset].ringcon);
    WriteReg(pProHW,pProslic->channel,USERSTAT,Si3226_Ring_Presets[preset].userstat);
    WriteRAM(pProHW,pProslic->channel,VCM_RING,Si3226_Ring_Presets[preset].vcm_ring);
    WriteRAM(pProHW,pProslic->channel,VCM_RING_FIXED,Si3226_Ring_Presets[preset].vcm_ring_fixed);
    WriteRAM(pProHW,pProslic->channel,DELTA_VCM,Si3226_Ring_Presets[preset].delta_vcm);

    WriteRAM(pProHW,pProslic->channel,DCDC_RNGTYPE,Si3226_Ring_Presets[preset].dcdc_rngtype);
	return RC_NONE;
}
#endif
/*
** Function: PROSLIC_ToneGenSetup
**
** Description: 
** configure tone generators
*/
#ifndef DISABLE_TONE_SETUP
int Si3226_ToneGenSetup (proslicChanType *pProslic, int preset){
	WriteRAM(pProHW,pProslic->channel,OSC1FREQ,Si3226_Tone_Presets[preset].osc1.freq);
	WriteRAM(pProHW,pProslic->channel,OSC1AMP,Si3226_Tone_Presets[preset].osc1.amp);
	WriteRAM(pProHW,pProslic->channel,OSC1PHAS,Si3226_Tone_Presets[preset].osc1.phas);
	WriteReg(pProHW,pProslic->channel,O1TAHI,(Si3226_Tone_Presets[preset].osc1.tahi));
	WriteReg(pProHW,pProslic->channel,O1TALO,(Si3226_Tone_Presets[preset].osc1.talo));
	WriteReg(pProHW,pProslic->channel,O1TIHI,(Si3226_Tone_Presets[preset].osc1.tihi));
	WriteReg(pProHW,pProslic->channel,O1TILO,(Si3226_Tone_Presets[preset].osc1.tilo));
	WriteRAM(pProHW,pProslic->channel,OSC2FREQ,Si3226_Tone_Presets[preset].osc2.freq);
	WriteRAM(pProHW,pProslic->channel,OSC2AMP,Si3226_Tone_Presets[preset].osc2.amp);
	WriteRAM(pProHW,pProslic->channel,OSC2PHAS,Si3226_Tone_Presets[preset].osc2.phas);
	WriteReg(pProHW,pProslic->channel,O2TAHI,(Si3226_Tone_Presets[preset].osc2.tahi));
	WriteReg(pProHW,pProslic->channel,O2TALO,(Si3226_Tone_Presets[preset].osc2.talo));
	WriteReg(pProHW,pProslic->channel,O2TIHI,(Si3226_Tone_Presets[preset].osc2.tihi));
	WriteReg(pProHW,pProslic->channel,O2TILO,(Si3226_Tone_Presets[preset].osc2.tilo));
	WriteReg(pProHW,pProslic->channel,OMODE,(Si3226_Tone_Presets[preset].omode));
	return 0;
}
#endif
/*
** Function: PROSLIC_FSKSetup
**
** Description: 
** configure fsk
*/
#ifndef DISABLE_FSK_SETUP
int Si3226_FSKSetup (proslicChanType *pProslic, int preset){
	uInt8 data; 

    WriteReg(pProHW,pProslic->channel,FSKDEPTH,0x08);
	WriteReg(pProHW,pProslic->channel,O1TAHI,0);
	WriteReg(pProHW,pProslic->channel,O1TIHI,0);
	WriteReg(pProHW,pProslic->channel,O1TILO,0);
	WriteReg(pProHW,pProslic->channel,O1TALO,0x13);

	data = ReadReg(pProHW,pProslic->channel,OMODE);
	if (Si3226_FSK_Presets[preset].eightBit)
		data |= 0x80;
	else 
		data &= ~(0x80);
	WriteReg(pProHW,pProslic->channel,FSKDEPTH,Si3226_FSK_Presets[preset].fskdepth);
	WriteReg(pProHW,pProslic->channel,OMODE,data);
	WriteRAM(pProHW,pProslic->channel,FSK01,Si3226_FSK_Presets[preset].fsk01);
	WriteRAM(pProHW,pProslic->channel,FSK10,Si3226_FSK_Presets[preset].fsk10);
	WriteRAM(pProHW,pProslic->channel,FSKAMP0,Si3226_FSK_Presets[preset].fskamp0);
	WriteRAM(pProHW,pProslic->channel,FSKAMP1,Si3226_FSK_Presets[preset].fskamp1);
	WriteRAM(pProHW,pProslic->channel,FSKFREQ0,Si3226_FSK_Presets[preset].fskfreq0);
	WriteRAM(pProHW,pProslic->channel,FSKFREQ1,Si3226_FSK_Presets[preset].fskfreq1);
	return 0;
}
#endif

/*
** Function: PROSLIC_DTMFDecodeSetup
**
** Description: 
** configure dtmf decode
*/
#ifndef DISABLE_DTMF_SETUP
int Si3226_DTMFDecodeSetup (proslicChanType *pProslic, int preset){
	
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B0_1,Si3226_DTMFDec_Presets[preset].dtmfdtf_b0_1);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B1_1,Si3226_DTMFDec_Presets[preset].dtmfdtf_b1_1);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B2_1,Si3226_DTMFDec_Presets[preset].dtmfdtf_b2_1);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_A1_1,Si3226_DTMFDec_Presets[preset].dtmfdtf_a1_1);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_A2_1,Si3226_DTMFDec_Presets[preset].dtmfdtf_a2_1);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B0_2,Si3226_DTMFDec_Presets[preset].dtmfdtf_b0_2);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B1_2,Si3226_DTMFDec_Presets[preset].dtmfdtf_b1_2);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B2_2,Si3226_DTMFDec_Presets[preset].dtmfdtf_b2_2);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_A1_2,Si3226_DTMFDec_Presets[preset].dtmfdtf_a1_2);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_A2_2,Si3226_DTMFDec_Presets[preset].dtmfdtf_a2_2);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B0_3,Si3226_DTMFDec_Presets[preset].dtmfdtf_b0_3);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B1_3,Si3226_DTMFDec_Presets[preset].dtmfdtf_b1_3);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_B2_3,Si3226_DTMFDec_Presets[preset].dtmfdtf_b2_3);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_A1_3,Si3226_DTMFDec_Presets[preset].dtmfdtf_a1_3);
	WriteRAM(pProHW,pProslic->channel,DTMFDTF_A2_3,Si3226_DTMFDec_Presets[preset].dtmfdtf_a2_3);
	return 0;
}
#endif

/*
** Function: PROSLIC_ZsynthSetup
**
** Description: 
** configure impedance synthesis
*/
#ifndef DISABLE_ZSYNTH_SETUP
int Si3226_ZsynthSetup (proslicChanType *pProslic, int preset){
	uInt8 lf;
    uInt8 cal_en = 0;
    uInt16 timer = 500;

	lf = ReadReg(pProHW,pProslic->channel,LINEFEED);
	WriteReg(pProHW,pProslic->channel,LINEFEED,0);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C0,Si3226_Impedance_Presets[preset].audioEQ.txaceq_c0);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C1,Si3226_Impedance_Presets[preset].audioEQ.txaceq_c1);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C2,Si3226_Impedance_Presets[preset].audioEQ.txaceq_c2);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C3,Si3226_Impedance_Presets[preset].audioEQ.txaceq_c3);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C0,Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c0);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C1,Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c1);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C2,Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c2);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C3,Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c3);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C2,Si3226_Impedance_Presets[preset].hybrid.ecfir_c2);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C3,Si3226_Impedance_Presets[preset].hybrid.ecfir_c3);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C4,Si3226_Impedance_Presets[preset].hybrid.ecfir_c4);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C5,Si3226_Impedance_Presets[preset].hybrid.ecfir_c5);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C6,Si3226_Impedance_Presets[preset].hybrid.ecfir_c6);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C7,Si3226_Impedance_Presets[preset].hybrid.ecfir_c7);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C8,Si3226_Impedance_Presets[preset].hybrid.ecfir_c8);
	WriteRAM(pProHW,pProslic->channel,ECFIR_C9,Si3226_Impedance_Presets[preset].hybrid.ecfir_c9);
	WriteRAM(pProHW,pProslic->channel,ECIIR_B0,Si3226_Impedance_Presets[preset].hybrid.ecfir_b0);
	WriteRAM(pProHW,pProslic->channel,ECIIR_B1,Si3226_Impedance_Presets[preset].hybrid.ecfir_b1);
	WriteRAM(pProHW,pProslic->channel,ECIIR_A1,Si3226_Impedance_Presets[preset].hybrid.ecfir_a1);
	WriteRAM(pProHW,pProslic->channel,ECIIR_A2,Si3226_Impedance_Presets[preset].hybrid.ecfir_a2);
	WriteRAM(pProHW,pProslic->channel,ZSYNTH_A1,Si3226_Impedance_Presets[preset].zsynth.zsynth_a1);
	WriteRAM(pProHW,pProslic->channel,ZSYNTH_A2,Si3226_Impedance_Presets[preset].zsynth.zsynth_a2);
	WriteRAM(pProHW,pProslic->channel,ZSYNTH_B1,Si3226_Impedance_Presets[preset].zsynth.zsynth_b1);
	WriteRAM(pProHW,pProslic->channel,ZSYNTH_B0,Si3226_Impedance_Presets[preset].zsynth.zsynth_b0);
	WriteRAM(pProHW,pProslic->channel,ZSYNTH_B2,Si3226_Impedance_Presets[preset].zsynth.zsynth_b2);
	WriteReg(pProHW,pProslic->channel,RA,Si3226_Impedance_Presets[preset].zsynth.ra);
	WriteRAM(pProHW,pProslic->channel,TXACGAIN,Si3226_Impedance_Presets[preset].txgain);
	WriteRAM(pProHW,pProslic->channel,RXACGAIN_SAVE,Si3226_Impedance_Presets[preset].rxgain);
	WriteRAM(pProHW,pProslic->channel,RXACGAIN,Si3226_Impedance_Presets[preset].rxgain);
	WriteRAM(pProHW,pProslic->channel,RXACHPF_B0_1,Si3226_Impedance_Presets[preset].rxachpf_b0_1);
	WriteRAM(pProHW,pProslic->channel,RXACHPF_B1_1,Si3226_Impedance_Presets[preset].rxachpf_b1_1);
	WriteRAM(pProHW,pProslic->channel,RXACHPF_A1_1,Si3226_Impedance_Presets[preset].rxachpf_a1_1);

    /*
    ** Scale based on desired gain plan
    */
    Si3226_dbgSetTXGain(pProslic,Si3226_Impedance_Presets[preset].txgain_db,preset,TXACGAIN_SEL);
    Si3226_dbgSetRXGain(pProslic,Si3226_Impedance_Presets[preset].rxgain_db,preset,RXACGAIN_SEL);
    Si3226_TXAudioGainSetup(pProslic,TXACGAIN_SEL);
    Si3226_RXAudioGainSetup(pProslic,RXACGAIN_SEL);


    /* 
    ** Perform Zcal in case OHT used (eg. no offhook event to trigger auto Zcal) 
    */
 	WriteReg(pProHW,pProslic->channel,CALR0,0x00);   
 	WriteReg(pProHW,pProslic->channel,CALR1,0x40);   
 	WriteReg(pProHW,pProslic->channel,CALR2,0x00); 
 	WriteReg(pProHW,pProslic->channel,CALR3,0x80);  /* start cal */

    /* Wait for zcal to finish */
    do {
        cal_en = ReadReg(pProHW,pProslic->channel,CALR3);
        Delay(pProTimer,1);
        timer--;
    }while((cal_en&0x80)&&(timer>0));  
     
	WriteReg(pProHW,pProslic->channel,LINEFEED,lf);

    if(timer > 0) return 0;
    else          return RC_CAL_TIMEOUT;

}
#endif

/*
** Function: PROSLIC_GciCISetup
**
** Description: 
** configure CI bits (GCI mode)
*/
#ifndef DISABLE_CI_SETUP
int Si3226_GciCISetup (proslicChanType *pProslic, int preset){
	WriteReg(pProHW,pProslic->channel,GCI_CI,Si3226_CI_Presets[preset].gci_ci);
	return 0;
}
#endif

/*
** Function: PROSLIC_AudioGainSetup
**
** Description: 
** configure audio gains
*/
#ifndef DISABLE_AUDIOGAIN_SETUP
int Si3226_TXAudioGainSetup (proslicChanType *pProslic, int preset){
	WriteRAM(pProHW,pProslic->channel,TXACGAIN,Si3226_audioGain_Presets[preset].acgain);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C0,Si3226_audioGain_Presets[preset].aceq_c0);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C1,Si3226_audioGain_Presets[preset].aceq_c1);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C2,Si3226_audioGain_Presets[preset].aceq_c2);
	WriteRAM(pProHW,pProslic->channel,TXACEQ_C3,Si3226_audioGain_Presets[preset].aceq_c3);
	return 0;
}

/*
** Function: PROSLIC_AudioGainSetup
**
** Description: 
** configure audio gains
*/
int Si3226_RXAudioGainSetup (proslicChanType *pProslic, int preset){
	WriteRAM(pProHW,pProslic->channel,RXACGAIN_SAVE,Si3226_audioGain_Presets[preset].acgain);
	WriteRAM(pProHW,pProslic->channel,RXACGAIN,Si3226_audioGain_Presets[preset].acgain);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C0,Si3226_audioGain_Presets[preset].aceq_c0);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C1,Si3226_audioGain_Presets[preset].aceq_c1);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C2,Si3226_audioGain_Presets[preset].aceq_c2);
	WriteRAM(pProHW,pProslic->channel,RXACEQ_C3,Si3226_audioGain_Presets[preset].aceq_c3);
	return 0;
}
#endif


/*
** Function: PROSLIC_AudioGainScale
**
** Description: 
** Multiply path gain by passed value for PGA and EQ scale (no reference to dB, 
** multiply by a scale factor)
*/
int Si3226_AudioGainScale (proslicChanType *pProslic, int preset, uInt32 pga_scale, uInt32 eq_scale,int rx_tx_sel){
 
	if(rx_tx_sel == TXACGAIN_SEL)
	{
		Si3226_audioGain_Presets[TXACGAIN_SEL].acgain = (Si3226_Impedance_Presets[preset].txgain/1000)*pga_scale;
		if (Si3226_Impedance_Presets[preset].audioEQ.txaceq_c0 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.txaceq_c0 |= 0xf0000000L;
		if (Si3226_Impedance_Presets[preset].audioEQ.txaceq_c1 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.txaceq_c1 |= 0xf0000000L;
		if (Si3226_Impedance_Presets[preset].audioEQ.txaceq_c2 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.txaceq_c2 |= 0xf0000000L;
		if (Si3226_Impedance_Presets[preset].audioEQ.txaceq_c3 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.txaceq_c3 |= 0xf0000000L;
		Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c0 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.txaceq_c0/1000)*eq_scale;
		Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c1 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.txaceq_c1/1000)*eq_scale;
		Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c2 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.txaceq_c2/1000)*eq_scale;
		Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c3 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.txaceq_c3/1000)*eq_scale;

		WriteRAM(pProHW,pProslic->channel,TXACGAIN,Si3226_audioGain_Presets[TXACGAIN_SEL].acgain);
		WriteRAM(pProHW,pProslic->channel,TXACEQ_C0,Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c0);
		WriteRAM(pProHW,pProslic->channel,TXACEQ_C1,Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c1);
		WriteRAM(pProHW,pProslic->channel,TXACEQ_C2,Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c2);
		WriteRAM(pProHW,pProslic->channel,TXACEQ_C3,Si3226_audioGain_Presets[TXACGAIN_SEL].aceq_c3);
	}
	else
	{
		Si3226_audioGain_Presets[RXACGAIN_SEL].acgain = (Si3226_Impedance_Presets[preset].rxgain/1000)*pga_scale;
		if (Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c0 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c0 |= 0xf0000000L;
		if (Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c1 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c1 |= 0xf0000000L;
		if (Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c2 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c2 |= 0xf0000000L;
		if (Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c3 & 0x10000000L)
			Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c3 |= 0xf0000000L;
		Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c0 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c0/1000)*eq_scale;
		Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c1 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c1/1000)*eq_scale;
		Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c2 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c2/1000)*eq_scale;
		Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c3 = ((int32)Si3226_Impedance_Presets[preset].audioEQ.rxaceq_c3/1000)*eq_scale;
	
		WriteRAM(pProHW,pProslic->channel,RXACGAIN_SAVE,Si3226_audioGain_Presets[RXACGAIN_SEL].acgain);
		WriteRAM(pProHW,pProslic->channel,RXACGAIN,Si3226_audioGain_Presets[RXACGAIN_SEL].acgain);
		WriteRAM(pProHW,pProslic->channel,RXACEQ_C0,Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c0);
		WriteRAM(pProHW,pProslic->channel,RXACEQ_C1,Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c1);
		WriteRAM(pProHW,pProslic->channel,RXACEQ_C2,Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c2);
		WriteRAM(pProHW,pProslic->channel,RXACEQ_C3,Si3226_audioGain_Presets[RXACGAIN_SEL].aceq_c3);
	}
    return 0;
}
int Si3226_TXAudioGainScale (proslicChanType *pProslic, int preset,uInt32 pga_scale, uInt32 eq_scale)
{
	return Si3226_AudioGainScale(pProslic,preset,pga_scale,eq_scale,TXACGAIN_SEL);
}
int Si3226_RXAudioGainScale (proslicChanType *pProslic, int preset,uInt32 pga_scale, uInt32 eq_scale)
{
	return Si3226_AudioGainScale(pProslic,preset,pga_scale,eq_scale,RXACGAIN_SEL);
}


/*
** Function: PROSLIC_DCFeedSetup
**
** Description: 
** configure dc feed
*/
#ifndef DISABLE_DCFEED_SETUP
int Si3226_DCFeedSetup (proslicChanType *pProslic, int preset){
	uInt8 lf;
	lf = ReadReg(pProHW,pProslic->channel,LINEFEED);
	WriteReg(pProHW,pProslic->channel,LINEFEED,0);
	WriteRAM(pProHW,pProslic->channel,SLOPE_VLIM,Si3226_DCfeed_Presets[preset].slope_vlim);
	WriteRAM(pProHW,pProslic->channel,SLOPE_RFEED,Si3226_DCfeed_Presets[preset].slope_rfeed);
	WriteRAM(pProHW,pProslic->channel,SLOPE_ILIM,Si3226_DCfeed_Presets[preset].slope_ilim);
	WriteRAM(pProHW,pProslic->channel,SLOPE_DELTA1,Si3226_DCfeed_Presets[preset].delta1);
	WriteRAM(pProHW,pProslic->channel,SLOPE_DELTA2,Si3226_DCfeed_Presets[preset].delta2);
	WriteRAM(pProHW,pProslic->channel,V_VLIM,Si3226_DCfeed_Presets[preset].v_vlim);
	WriteRAM(pProHW,pProslic->channel,V_RFEED,Si3226_DCfeed_Presets[preset].v_rfeed);
	WriteRAM(pProHW,pProslic->channel,V_ILIM,Si3226_DCfeed_Presets[preset].v_ilim);
	WriteRAM(pProHW,pProslic->channel,CONST_RFEED,Si3226_DCfeed_Presets[preset].const_rfeed);
	WriteRAM(pProHW,pProslic->channel,CONST_ILIM,Si3226_DCfeed_Presets[preset].const_ilim);
	WriteRAM(pProHW,pProslic->channel,I_VLIM,Si3226_DCfeed_Presets[preset].i_vlim);
	WriteRAM(pProHW,pProslic->channel,LCRONHK,Si3226_DCfeed_Presets[preset].lcronhk);
	WriteRAM(pProHW,pProslic->channel,LCROFFHK,Si3226_DCfeed_Presets[preset].lcroffhk);
	WriteRAM(pProHW,pProslic->channel,LCRDBI,Si3226_DCfeed_Presets[preset].lcrdbi);
	WriteRAM(pProHW,pProslic->channel,LONGHITH,Si3226_DCfeed_Presets[preset].longhith);
	WriteRAM(pProHW,pProslic->channel,LONGLOTH,Si3226_DCfeed_Presets[preset].longloth);
	WriteRAM(pProHW,pProslic->channel,LONGDBI,Si3226_DCfeed_Presets[preset].longdbi);
	WriteRAM(pProHW,pProslic->channel,LCRMASK,Si3226_DCfeed_Presets[preset].lcrmask);
	WriteRAM(pProHW,pProslic->channel,LCRMASK_POLREV,Si3226_DCfeed_Presets[preset].lcrmask_polrev);
	WriteRAM(pProHW,pProslic->channel,LCRMASK_STATE,Si3226_DCfeed_Presets[preset].lcrmask_state);
	WriteRAM(pProHW,pProslic->channel,LCRMASK_LINECAP,Si3226_DCfeed_Presets[preset].lcrmask_linecap);
	WriteRAM(pProHW,pProslic->channel,VCM_OH,Si3226_DCfeed_Presets[preset].vcm_oh);
	WriteRAM(pProHW,pProslic->channel,VOV_BAT,Si3226_DCfeed_Presets[preset].vov_bat);
	WriteRAM(pProHW,pProslic->channel,VOV_GND,Si3226_DCfeed_Presets[preset].vov_gnd);
	WriteReg(pProHW,pProslic->channel,LINEFEED,lf);
	return 0;
}
#endif

/*
** Function: PROSLIC_GPIOSetup
**
** Description: 
** configure gpio
*/
#ifndef DISABLE_GPIO_SETUP
int Si3226_GPIOSetup (proslicChanType *pProslic){
	uInt8 data;
	data = ReadReg(pProHW,pProslic->channel,GPIO);
	data |= Si3226_GPIO_Configuration.outputEn << 4;
	WriteReg(pProHW,pProslic->channel,GPIO,data);
	data = Si3226_GPIO_Configuration.analog << 4;
	data |= Si3226_GPIO_Configuration.direction;
	WriteReg(pProHW,pProslic->channel,GPIO_CFG1,data);
	data = Si3226_GPIO_Configuration.manual << 4;
	data |= Si3226_GPIO_Configuration.polarity;
	WriteReg(pProHW,pProslic->channel,GPIO_CFG2,data);
	data |= Si3226_GPIO_Configuration.openDrain;
	WriteReg(pProHW,pProslic->channel,GPIO_CFG3,data);
	WriteReg(pProHW,pProslic->channel,BATSELMAP,Si3226_GPIO_Configuration.batselmap);
	return 0;
}
#endif

/*
** Function: PROSLIC_PCMSetup
**
** Description: 
** configure pcm
*/
#ifndef DISABLE_PCM_SETUP
int Si3226_PCMSetup (proslicChanType *pProslic, int preset){
	uInt8 regTemp;
	
	
    /* TODO:  Remove hardcoded coefficients for wideband mode */
	if (Si3226_PCM_Presets[preset].widebandEn){
		regTemp = ReadReg(pProHW,pProslic->channel,DIGCON);
		WriteReg(pProHW,pProslic->channel,DIGCON,regTemp|0xC);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B0_1,0x27EA83L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B1_1,0x27EA83L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A1_1,0x487977EL);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B0_2,0x8000000L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B1_2,0x7E8704DL);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B2_2,0x8000000L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A1_2,0x368C302L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A2_2,0x18EBB1A4L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B0_3,0x8000000L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B1_3,0x254C75AL);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B2_3,0x7FFFFFFL);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A1_3,0x639A165L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A2_3,0x1B6738A0L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B0_1,0x4FD507L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B1_1,0x4FD507L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A1_1,0x487977EL);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B0_2,0x8000000L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B1_2,0x7E8704DL);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B2_2,0x8000000L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A1_2,0x368C302L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A2_2,0x18EBB1A4L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B0_3,0x8000000L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B1_3,0x254C75AL);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B2_3,0x7FFFFFFL);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A1_3,0x639A165L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A2_3,0x1B6738A0L);
		regTemp = ReadReg(pProHW,pProslic->channel,ENHANCE);
		WriteReg(pProHW,pProslic->channel,ENHANCE,regTemp|1);
	} else {
		regTemp = ReadReg(pProHW,pProslic->channel,DIGCON);
		WriteReg(pProHW,pProslic->channel,DIGCON,regTemp&~(0xC));
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B0_1,0x3538E80L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B1_1,0x3538E80L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A1_1,0x1AA9100L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B0_2,0x216D100L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B1_2,0x2505400L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B2_2,0x216D100L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A1_2,0x2CB8100L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A2_2,0x1D7FA500L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B0_3,0x2CD9B00L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B1_3,0x1276D00L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_B2_3,0x2CD9B00L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A1_3,0x2335300L);
		WriteRAM(pProHW,pProslic->channel,TXACIIR_A2_3,0x19D5F700L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B0_1,0x6A71D00L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B1_1,0x6A71D00L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A1_1,0x1AA9100L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B0_2,0x216D100L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B1_2,0x2505400L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B2_2,0x216D100L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A1_2,0x2CB8100L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A2_2,0x1D7FA500L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B0_3,0x2CD9B00L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B1_3,0x1276D00L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_B2_3,0x2CD9B00L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A1_3,0x2335300L);
		WriteRAM(pProHW,pProslic->channel,RXACIIR_A2_3,0x19D5F700L);
		regTemp = ReadReg(pProHW,pProslic->channel,ENHANCE);
		WriteReg(pProHW,pProslic->channel,ENHANCE,regTemp&~(1));
	}

    /*
    ** Only update format and control options
    ** Enable/Disable and timeslots handled 
    ** with API control functions
    */

	regTemp = Si3226_PCM_Presets[preset].pcmFormat;
	regTemp |= Si3226_PCM_Presets[preset].pcm_tri << 5;
	regTemp |= Si3226_PCM_Presets[preset].alaw_inv << 2;
	WriteReg(pProHW,pProslic->channel,PCMMODE,regTemp);
	regTemp = ReadReg(pProHW,pProslic->channel,PCMTXHI);
	regTemp &= 3;
	regTemp |= Si3226_PCM_Presets[preset].tx_edge<<4;
	WriteReg(pProHW,pProslic->channel,PCMTXHI,regTemp);

	return 0;
}
#endif

/*
**
** PROSLIC CONTROL FUNCTIONS
**
*/

/*
** Function: PROSLIC_SetLinefeedStatus
**
** Description: 
** Sets linefeed state
*/
int Si3226_SetLinefeedStatus (proslicChanType *pProslic,uInt8 newLinefeed){
	WriteReg (pProHW, pProslic->channel, LINEFEED,newLinefeed);
	return 0;
}

/*
** Function: PROSLIC_MWI
**
** Description: 
** implements message waiting indicator
*/
int Si3226_MWI (proslicChanType *pProslic,uInt8 lampOn){
	/*message waiting (neon flashing) requires modifications to vbath_expect and slope_vlim.
	The old values are restored to turn off the lamp. We assume all channels set up the same.
	During off-hook event lamp must be disabled manually. */
	static int32 vbath_save = 0;
	static int32 slope_vlim_save = 0;
	uInt8 hkStat; int32 slope_vlim_tmp;
	slope_vlim_tmp = ReadRAM(pProHW,pProslic->channel,SLOPE_VLIM);
	Si3226_ReadHookStatus(pProslic,&hkStat);

	if (lampOn && (hkStat == PROSLIC_OFFHOOK) ) {/*cant neon flash during offhook*/
#ifdef ENABLE_DEBUG
		if (pProslic->debugMode)	
			LOGPRINT ("%sMWI cannot operate offhook\n", LOGPRINT_PREFIX);
#endif
		return RC_LINE_IN_USE;
	}

	if (lampOn) {
		if (slope_vlim_tmp != 0x8000000L) { /*check we're not already on*/
			vbath_save = ReadRAM(pProHW,pProslic->channel,VBATH_EXPECT);
			slope_vlim_save = slope_vlim_tmp;
		}
		WriteRAM(pProHW,pProslic->channel,VBATH_EXPECT,0x7AE147AL);/*120V*/
		WriteRAM(pProHW,pProslic->channel,SLOPE_VLIM,0x8000000L);
	} else {
		if (vbath_save != 0) { /*check we saved some valid value first*/
			WriteRAM(pProHW,pProslic->channel,VBATH_EXPECT,vbath_save);
			WriteRAM(pProHW,pProslic->channel,SLOPE_VLIM,slope_vlim_save);
		}
	}

	return RC_NONE;
}

/*
** Function: PROSLIC_dbgSetDCFeed
**
** Description: 
** provisionally function for setting up
** dcfeed given desired open circuit voltage 
** and loop current.
*/
int Si3226_dbgSetDCFeed (proslicChanType *pProslic, uInt32 v_vlim_val, uInt32 i_ilim_val, int32 preset){
/* Note:  * needs more descriptive return codes in the event of an out of range argument */
    uInt16 vslope = 160;
    uInt16 rslope = 720;
    uInt32 vscale1 = 1386; 
    uInt32 vscale2 = 1422;   /* 1386x1422 = 1970892 broken down to minimize trunc err */
    uInt32 iscale1 = 913;
    uInt32 iscale2 = 334;    /* 913x334 = 304942 */
    uInt32 i_rfeed_val, v_rfeed_val, const_rfeed_val, i_vlim_val, const_ilim_val, v_ilim_val;
    int32 signedVal;

    SILABS_UNREFERENCED_PARAMETER(pProslic);

    /* Assumptions must be made to minimize computations.  This limits the
    ** range of available settings, but should be more than adequate for
    ** short loop applications.
    **
    ** Assumtions:
    ** 
    ** SLOPE_VLIM      =>  160ohms
    ** SLOPE_RFEED     =>  720ohms
    ** I_RFEED         =>  3*I_ILIM/4
    ** 
    ** With these assumptions, the DC Feed parameters now become 
    **
    ** Inputs:      V_VLIM, I_ILIM
    ** Constants:   SLOPE_VLIM, SLOPE_ILIM, SLOPE_RFEED, SLOPE_DELTA1, SLOPE_DELTA2
    ** Outputs:     V_RFEED, V_ILIM, I_VLIM, CONST_RFEED, CONST_ILIM
    **
    */

    /* Validate arguements */
    if((i_ilim_val < 15)||(i_ilim_val > 45)) return 1;   /* need error code */
    if((v_vlim_val < 30)||(v_vlim_val > 52)) return 1;   /* need error code */

    /* Calculate voltages in mV and currents in uA */
    v_vlim_val *= 1000;
    i_ilim_val *= 1000;

    /* I_RFEED */
    i_rfeed_val = (3*i_ilim_val)/4;

    /* V_RFEED */
    v_rfeed_val = v_vlim_val - (i_rfeed_val*vslope)/1000;

    /* V_ILIM */ 
    v_ilim_val = v_rfeed_val - (rslope*(i_ilim_val - i_rfeed_val))/1000;

    /* I_VLIM */
    i_vlim_val = (v_vlim_val*1000)/4903;

    /* CONST_RFEED */
    signedVal = v_rfeed_val * (i_ilim_val - i_rfeed_val);
    signedVal /= (v_rfeed_val - v_ilim_val);
    signedVal = i_rfeed_val + signedVal;

    /*  signedVal in uA here */
    signedVal *= iscale1;
    signedVal /= 100;
    signedVal *= iscale2;
    signedVal /= 10;

    if(signedVal < 0)
    {
        const_rfeed_val = (signedVal)+ (1L<<29);
    }
    else
    {
        const_rfeed_val = signedVal & 0x1FFFFFFF;
    }

    /* CONST_ILIM */
    const_ilim_val = i_ilim_val;

    /* compute RAM values */
    v_vlim_val *= vscale1;
    v_vlim_val /= 100;
    v_vlim_val *= vscale2;
    v_vlim_val /= 10;

    v_rfeed_val *= vscale1;
    v_rfeed_val /= 100;
    v_rfeed_val *= vscale2;
    v_rfeed_val /= 10;

    v_ilim_val *= vscale1;
    v_ilim_val /= 100;
    v_ilim_val *= vscale2;
    v_ilim_val /= 10;

    const_ilim_val *= iscale1;
    const_ilim_val /= 100;
    const_ilim_val *= iscale2;
    const_ilim_val /= 10;

    i_vlim_val *= iscale1;
    i_vlim_val /= 100;
    i_vlim_val *= iscale2;
    i_vlim_val /= 10;

	Si3226_DCfeed_Presets[preset].slope_vlim = 0x18842BD7L;
	Si3226_DCfeed_Presets[preset].slope_rfeed = 0x1E8886DEL;
	Si3226_DCfeed_Presets[preset].slope_ilim = 0x40A0E0L;
	Si3226_DCfeed_Presets[preset].delta1 = 0x1EABA1BFL;
	Si3226_DCfeed_Presets[preset].delta2 = 0x1EF744EAL;
	Si3226_DCfeed_Presets[preset].v_vlim = v_vlim_val;
	Si3226_DCfeed_Presets[preset].v_rfeed = v_rfeed_val;
	Si3226_DCfeed_Presets[preset].v_ilim = v_ilim_val;
	Si3226_DCfeed_Presets[preset].const_rfeed = const_rfeed_val;
	Si3226_DCfeed_Presets[preset].const_ilim = const_ilim_val;
	Si3226_DCfeed_Presets[preset].i_vlim = i_vlim_val;
	
	return 0;
}

/*
** Function: PROSLIC_dbgSetDCFeedVopen
**
** Description: 
** provisionally function for setting up
** dcfeed given desired open circuit voltage.
** Entry I_ILIM value will be used.
*/
int Si3226_dbgSetDCFeedVopen (proslicChanType *pProslic, uInt32 v_vlim_val, int32 preset)
{
uInt32 i_ilim_val;
uInt32 iscale1 = 913;
uInt32 iscale2 = 334;    /* 913x334 = 304942 */

    /* Read present CONST_ILIM value */
    i_ilim_val = Si3226_DCfeed_Presets[preset].const_ilim;


    i_ilim_val /= iscale2;
    i_ilim_val /= iscale1;

    return Si3226_dbgSetDCFeed(pProslic,v_vlim_val,i_ilim_val,preset);
}

/*
** Function: PROSLIC_dbgSetDCFeedIloop
**
** Description: 
** provisionally function for setting up
** dcfeed given desired loop current.
** Entry V_VLIM value will be used.
*/
int Si3226_dbgSetDCFeedIloop (proslicChanType *pProslic, uInt32 i_ilim_val, int32 preset)
{
uInt32 v_vlim_val;
uInt32 vscale1 = 1386; 
uInt32 vscale2 = 1422;   /* 1386x1422 = 1970892 broken down to minimize trunc err */

    /* Read present V_VLIM value */
    v_vlim_val = Si3226_DCfeed_Presets[preset].v_vlim;

    v_vlim_val /= vscale2;
    v_vlim_val /= vscale1;

    return Si3226_dbgSetDCFeed(pProslic,v_vlim_val,i_ilim_val, preset);
}


typedef struct
{
    uInt8   freq;
    ramData ringfr;      /* trise scale for trap */
    uInt32  ampScale;
} ProSLIC_SineRingFreqLookup;

typedef struct
{
    uInt8    freq;
    ramData  rtacth;
    ramData rtper;
    ramData rtdb;
} ProSLIC_SineRingtripLookup;

typedef struct
{
    uInt8   freq;
    uInt16  cfVal[6];
} ProSLIC_TrapRingFreqLookup;

typedef struct
{
    uInt8   freq;
    ramData rtper;
    ramData rtdb;
    uInt32  rtacth[6];
} ProSLIC_TrapRingtripLookup;


/*
** Function: PROSLIC_dbgRingingSetup
**
** Description: 
** Provision function for setting up
** Ring type, frequency, amplitude and dc offset.
** Main use will be by peek/poke applications.
*/
int Si3226_dbgSetRinging (proslicChanType *pProslic, ProSLIC_dbgRingCfg *ringCfg, int preset){
    int errVal,i=0;
    uInt32 vScale = 1608872L;   /* (2^28/170.25)*((100+4903)/4903) */
    ramData dcdcVminTmp;

    const ProSLIC_SineRingFreqLookup sineRingFreqTable[] =
/*  Freq RINGFR, vScale */
        {{15, 0x7F6E930L, 18968L},
         {16, 0x7F5A8E0L, 20234L},
         {20, 0x7EFD9D5L, 25301L},
         {22, 0x7EC770AL, 27843L},
         {23, 0x7EAA6E2L, 29113L},
         {25, 0x7E6C925L, 31649L},
         {30, 0x7DBB96BL, 38014L},
         {34, 0x7D34155L, 42270L}, /* Actually 33.33Hz */
         {35, 0x7CEAD72L, 44397L},
         {40, 0x7BFA887L, 50802L},
         {45, 0x7AEAE74L, 57233L},
         {50, 0x79BC384L, 63693L},
         {0,0,0}}; /* terminator */

    const ProSLIC_SineRingtripLookup sineRingtripTable[] =
/*  Freq rtacth */
        { {15, 11440000L, 0x6A000L, 0x4000L },
          {16, 10810000L, 0x64000L, 0x4000L },
          {20, 8690000L,  0x50000L, 0x8000L }, 
          {22, 7835000L,  0x48000L, 0x8000L },
          {23, 7622000L,  0x46000L, 0x8000L }, 
          {25, 6980000L,  0x40000L, 0xA000L }, 
          {30, 5900000L,  0x36000L, 0xA000L }, 
          {34, 10490000L, 0x60000L, 0x6000L }, /* Actually 33.33 */
          {35, 10060000L, 0x5C000L, 0x6000L }, 
          {40, 8750000L,  0x50000L, 0x8000L }, 
          {45, 7880000L,  0x48000L, 0x8000L }, 
          {50, 7010000L,  0x40000L, 0xA000L }, 
          {0,0L}}; /* terminator */

    const ProSLIC_TrapRingFreqLookup trapRingFreqTable[] =
/*  Freq multCF11 multCF12 multCF13 multCF14 multCF15 multCF16*/
    {
        {15, {69,122, 163, 196, 222,244}},
        {16, {65,115, 153, 184, 208,229}},
        {20, {52,92, 122, 147, 167,183}},
        {22, {47,83, 111, 134, 152,166}},
        {23, {45,80, 107, 128, 145,159}},
        {25, {42,73, 98, 118, 133,146}},
        {30, {35,61, 82, 98, 111,122}},
        {34, {31,55, 73, 88, 100,110}},
        {35, {30,52, 70, 84, 95,104}},
        {40, {26,46, 61, 73, 83,91}},
        {45, {23,41, 54, 65, 74,81}},
        {50, {21,37, 49, 59, 67,73}},
        {0,{0L,0L,0L,0L}} /* terminator */
    }; 


    const ProSLIC_TrapRingtripLookup trapRingtripTable[] =
/*  Freq rtper rtdb rtacthCR11 rtacthCR12 rtacthCR13 rtacthCR14 rtacthCR15 rtacthCR16*/
    {
        {15, 0x6A000L,  0x4000L, {16214894L, 14369375L, 12933127L, 11793508L, 10874121L, 10121671L}},
        {16, 0x64000L,  0x4000L, {15201463L, 13471289L, 12124806L, 11056414L, 10194489L, 9489067L}},
        {20, 0x50000L,  0x6000L, {12161171L, 10777031L, 9699845L, 8845131L, 8155591L, 7591253L}},
        {22, 0x48000L,  0x6000L, {11055610L, 9797301L, 8818041L, 8041028L, 7414174L, 6901139L}},
        {23, 0x46000L,  0x6000L, {10574931L, 9371331L, 8434648L, 7691418L, 7091818L, 6601090L}},
        {25, 0x40000L,  0x8000L, {9728937L, 8621625L, 7759876L, 7076105L, 6524473L, 6073003L}},
        {30, 0x36000L,  0x8000L, {8107447L, 7184687L, 6466563L, 5896754L, 5437061L, 5060836L}},
        {34, 0x60000L,  0x6000L, {7297432L, 6466865L, 5820489L, 5307609L, 4893844L, 4555208L}},
        {35, 0x5C000L,  0x6000L, {6949240L, 6158303L, 5542769L, 5054361L, 4660338L, 4337859L}},
        {40, 0x50000L,  0x6000L, {6080585L, 5388516L, 4849923L, 4422565L, 4077796L, 3795627L}},
        {45, 0x48000L,  0x6000L, {5404965L, 4789792L, 4311042L, 3931169L, 3624707L, 3373890L}},
        {50, 0x40000L,  0x8000L, {4864468L, 4310812L, 3879938L, 3538052L, 3262236L, 3036501L}},
        {0,0x0L, 0x0L, {0L,0L,0L,0L}} /* terminator */
    }; 

    SILABS_UNREFERENCED_PARAMETER(pProslic);

    errVal = 0;

    switch(ringCfg->ringtype)
    {
    case ProSLIC_RING_SINE:
        i=0;
        do
        {
            if(sineRingFreqTable[i].freq >= ringCfg->freq) 
            {
                break;
            }
            i++;
        } while (sineRingFreqTable[i].freq);

        /* Set to maximum value if exceeding maximum value from table */
        if(sineRingFreqTable[i].freq == 0)
        {
            i--;
            errVal = 1;
        }

        /* Update RINGFR RINGAMP, RINGOFFSET, and RINGCON */
        Si3226_Ring_Presets[preset].freq = sineRingFreqTable[i].ringfr;
        Si3226_Ring_Presets[preset].amp = ringCfg->amp * sineRingFreqTable[i].ampScale;
        Si3226_Ring_Presets[preset].offset = ringCfg->offset * vScale;
        Si3226_Ring_Presets[preset].phas = 0L;

        /* Don't alter anything in RINGCON other than clearing the TRAP bit */
        Si3226_Ring_Presets[preset].ringcon &= 0xFE;

        Si3226_Ring_Presets[preset].rtper = sineRingtripTable[i].rtper;
        Si3226_Ring_Presets[preset].rtacdb = sineRingtripTable[i].rtdb;
        Si3226_Ring_Presets[preset].rtdcdb = sineRingtripTable[i].rtdb;
        Si3226_Ring_Presets[preset].rtdcth = 0xFFFFFFFL;
        Si3226_Ring_Presets[preset].rtacth = sineRingtripTable[i].rtacth;
        break;

    case ProSLIC_RING_TRAP_CF11:  
    case ProSLIC_RING_TRAP_CF12:     
    case ProSLIC_RING_TRAP_CF13: 
    case ProSLIC_RING_TRAP_CF14: 
    case ProSLIC_RING_TRAP_CF15:  
    case ProSLIC_RING_TRAP_CF16:  
        i=0;
        do
        {
            if(trapRingFreqTable[i].freq >= ringCfg->freq) 
            {
                break;
            }
            i++;
        } while (trapRingFreqTable[i].freq);

        /* Set to maximum value if exceeding maximum value from table */
        if(trapRingFreqTable[i].freq == 0)
        {
            i--;
            errVal = 1;
        }

        /* Update RINGFR RINGAMP, RINGOFFSET, and RINGCON */
        Si3226_Ring_Presets[preset].amp = ringCfg->amp * vScale;
        Si3226_Ring_Presets[preset].freq = Si3226_Ring_Presets[preset].amp/trapRingFreqTable[i].cfVal[ringCfg->ringtype];
        Si3226_Ring_Presets[preset].offset = ringCfg->offset * vScale;
        Si3226_Ring_Presets[preset].phas = 262144000L/trapRingFreqTable[i].freq;

        /* Don't alter anything in RINGCON other than setting the TRAP bit */
        Si3226_Ring_Presets[preset].ringcon |= 0x01; 

        /* RTPER and debouce timers  */
        Si3226_Ring_Presets[preset].rtper = trapRingtripTable[i].rtper;
        Si3226_Ring_Presets[preset].rtacdb = trapRingtripTable[i].rtdb;
        Si3226_Ring_Presets[preset].rtdcdb = trapRingtripTable[i].rtdb;  


        Si3226_Ring_Presets[preset].rtdcth = 0xFFFFFFFL;
        Si3226_Ring_Presets[preset].rtacth = trapRingtripTable[i].rtacth[ringCfg->ringtype];


        break;
    }

    /* 
    ** DCDC tracking sluggish under light load at higher ring freq.
    ** Reduce tracking depth above 40Hz.  This should have no effect
    ** if using the Buck-Boost architecture.
    */
    if((sineRingFreqTable[i].freq >= 40)||(Si3226_General_Configuration.bomOpt == BO_DCDC_BUCK_BOOST))
    {
        dcdcVminTmp = ringCfg->amp + ringCfg->offset;
        dcdcVminTmp *= 1000;
        dcdcVminTmp *= SCALE_V_MADC;
        Si3226_Ring_Presets[preset].dcdc_vref_min_rng = dcdcVminTmp;
    }
    else
    {
        Si3226_Ring_Presets[preset].dcdc_vref_min_rng = 0x1800000L;
    }

    return errVal;

}



typedef struct
{
    int32   gain;
    uInt32 scale;
} ProSLIC_GainScaleLookup;

#define EXTENDED_GAIN_MAX 9
#define GAIN_MAX 6
#define GAIN_MIN -30
#ifndef ENABLE_HIRES_GAIN
static int Si3226_dbgSetGain (proslicChanType *pProslic, int32 gain, int impedance_preset, int tx_rx_sel){
    int errVal = 0;
    int32 i;
    int32 gain_pga, gain_eq;
    const ProSLIC_GainScaleLookup gainScaleTable[] =
/*  gain, scale=10^(gain/20) */
    {     
		{-30, 32},
		{-29, 35},
		{-28, 40},
		{-27, 45},
		{-26, 50},
		{-25, 56},
		{-24, 63},
		{-23, 71},
		{-22, 79},
		{-21, 89},
		{-20, 100},
		{-19, 112},
		{-18, 126},
		{-17, 141},
        {-16, 158}, 
        {-15, 178}, 
        {-14, 200}, 
        {-13, 224}, 
        {-12, 251}, 
        {-11, 282}, 
        {-10, 316}, 
        {-9, 355}, 
        {-8, 398}, 
        {-7, 447}, 
        {-6, 501},
        {-5, 562},
        {-4, 631},
        {-3, 708},
        {-2, 794},
        {-1, 891},
        {0, 1000},
        {1, 1122},
        {2, 1259},
        {3, 1413},
        {4, 1585},
        {5, 1778},
        {6, 1995},
        {0xff,0}  /* terminator */
    }; 
 
/* 
** 5.4.0 - Removed relative gain scaling. to support automatic adjustment based on
**         gain plan provided in txgain_db and rxgain_db.  It is presumed that all
**         coefficients were generated for 0dB/0dB gain and the txgain_db and rxgain_db
**         parameters will be used to scale the gain using the existing gain provisioning
**         infrastructure when the zsynth preset is loaded.  This function will ignore 
**         the txgain_db and rxgain_db parameters and scale absolute gain presuming a
**         0dB/0dB coefficient set.
*/

/*
** 6.0.0 - Modifying where gain/attenuation is placed to minimize clipping.
**
**         RX Path:   -30dB < gain <  0dB -   All in RXACGAIN 
**                      0dB < gain <  6dB -   All in RXACEQ
**
**         TX Path:   -30dB < gain <  0dB -   All in TXACEQ
**                      0dB < gain <  6dB -   All in TXACGAIN
*/

    /* Test against max gain */
    if (gain > EXTENDED_GAIN_MAX)
	{
		errVal = RC_GAIN_OUT_OF_RANGE;
#ifdef ENABLE_DEBUG
		if(pProslic->debugMode)
		{
			LOGPRINT("%sdbgSetGain : Gain %d out of range\n", LOGPRINT_PREFIX, (int)gain);
		}
#endif
		gain = EXTENDED_GAIN_MAX; /* Clamp to maximum */
	}

	/* Test against min gain */
    if (gain < GAIN_MIN)
	{
		errVal = RC_GAIN_OUT_OF_RANGE;
#ifdef ENABLE_DEBUG
		if(pProslic->debugMode)
		{
			LOGPRINT("%sdbgSetGain : Gain %d out of range\n", LOGPRINT_PREFIX, (int)gain);
		}
#endif
		gain = GAIN_MIN; /* Clamp to minimum */
	}

	/* Distribute gain */
	if(gain == 0)
	{
		gain_pga = 0;
		gain_eq = 0;
	}
	else if(gain > GAIN_MAX)
	{
		if(tx_rx_sel == TXACGAIN_SEL)
		{
			gain_pga = GAIN_MAX;
			gain_eq = gain - GAIN_MAX;
		}
		else
		{
			gain_pga = gain - GAIN_MAX;
			gain_eq = GAIN_MAX;
		}
	}	
	else if(gain > 0)
	{
		if(tx_rx_sel == TXACGAIN_SEL)
		{
			gain_pga = gain;
			gain_eq  = 0;
		}
		else
		{
			gain_pga = 0;
			gain_eq = gain;
		}
	}
	else
	{
		if(tx_rx_sel == TXACGAIN_SEL)
		{
			gain_pga = 0;
			gain_eq  = gain;
		}
		else
		{
			gain_pga = gain;
			gain_eq = 0;
		}

	}
   
    /* 
	** Lookup PGA Appopriate PGA Gain
	*/
    i=0;
    do
    {
        if(gainScaleTable[i].gain >= gain_pga) 
        {
                
            break;
        }
        i++;
    } while (gainScaleTable[i].gain!=0xff);

    /* Set to maximum value if exceeding maximum value from table */
    if(gainScaleTable[i].gain == 0xff)
    {
        i--;
        errVal = RC_GAIN_DELTA_TOO_LARGE;
    }

    if(tx_rx_sel == TXACGAIN_SEL)
    {
        Si3226_audioGain_Presets[0].acgain = (Si3226_Impedance_Presets[impedance_preset].txgain/1000)*gainScaleTable[i].scale;
    }
    else
    {
        Si3226_audioGain_Presets[1].acgain = (Si3226_Impedance_Presets[impedance_preset].rxgain/1000)*gainScaleTable[i].scale;
    }

    /* 
	** Lookup EQ Gain
	*/
    i=0;
    do
    {
        if(gainScaleTable[i].gain >= gain_eq) 
        {
            break;
        }
        i++;
    } while (gainScaleTable[i].gain!=0xff);

    /* Set to maximum value if exceeding maximum value from table */
    if(gainScaleTable[i].gain == 0xff)
    {
        i--;
        errVal = RC_GAIN_DELTA_TOO_LARGE;
    }

    if(tx_rx_sel == TXACGAIN_SEL)
    {
        /*sign extend negative numbers*/
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c0 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c0 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c1 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c1 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c2 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c2 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c3 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c3 |= 0xf0000000L;

        Si3226_audioGain_Presets[0].aceq_c0 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c0/1000)*gainScaleTable[i].scale;
        Si3226_audioGain_Presets[0].aceq_c1 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c1/1000)*gainScaleTable[i].scale;
        Si3226_audioGain_Presets[0].aceq_c2 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c2/1000)*gainScaleTable[i].scale;
        Si3226_audioGain_Presets[0].aceq_c3 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c3/1000)*gainScaleTable[i].scale;
    }
    else
    {
        /*sign extend negative numbers*/
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c0 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c0 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c1 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c1 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c2 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c2 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c3 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c3 |= 0xf0000000L;

        Si3226_audioGain_Presets[1].aceq_c0 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c0/1000)*gainScaleTable[i].scale;
        Si3226_audioGain_Presets[1].aceq_c1 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c1/1000)*gainScaleTable[i].scale;
        Si3226_audioGain_Presets[1].aceq_c2 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c2/1000)*gainScaleTable[i].scale;
        Si3226_audioGain_Presets[1].aceq_c3 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c3/1000)*gainScaleTable[i].scale;
    }


    return errVal;
}
#else /* ENABLE_HIRES_GAIN */
/* Same as Si3226_dbgSetGain() except gain is expressed in dB*10 to achieve 0.1dB resolution */
static int Si3226_dbgSetGainHiRes (proslicChanType *pProslic, int32 gain, int impedance_preset, int tx_rx_sel){
    int errVal = 0;
    int32 i;
	int32 coarseGainIndex, fineGainIndex;
    int32 gain_pga, gain_eq;
	int32 coarseGain, fineGain;
	int32 tmp;
    const ProSLIC_GainScaleLookup coarseGainScaleTable[] =  /*  gain, scale=10^(gain/20) */
    { 
		{-30, 32},
		{-29, 35},
		{-28, 40},
		{-27, 45},
		{-26, 50},
		{-25, 56},
		{-24, 63},
		{-23, 71},
		{-22, 79},
		{-21, 89},
		{-20, 100},
		{-19, 112},
		{-18, 126},
		{-17, 141},
        {-16, 158}, 
        {-15, 178}, 
        {-14, 200}, 
        {-13, 224}, 
        {-12, 251}, 
        {-11, 282}, 
        {-10, 316}, 
        {-9, 355}, 
        {-8, 398}, 
        {-7, 447}, 
        {-6, 501},
        {-5, 562},
        {-4, 631},
        {-3, 708},
        {-2, 794},
        {-1, 891},
        {0, 1000},
        {1, 1122},
        {2, 1259},
        {3, 1413},
        {4, 1585},
        {5, 1778},
        {6, 1995},
        {0xff,0}  /* terminator */
    }; 

	const ProSLIC_GainScaleLookup fineGainScaleTable[] =  /*  gain, scale=10^(gain/20) */
    { 
        {-9, 902}, 
        {-8, 912}, 
        {-7, 923}, 
        {-6, 933},
        {-5, 944},
        {-4, 955},
        {-3, 966},
        {-2, 977},
        {-1, 989},
        {0, 1000},
        {1, 1012},
        {2, 1023},
        {3, 1035},
        {4, 1047},
        {5, 1059},
        {6, 1072},
        {7, 1084},
        {8, 1096},
        {9, 1109},
		{0xff,0}  /* terminator */
	};

/*
** 6.0.0 - Modifying where gain/attenuation is placed to minimize clipping.
**
**         RX Path:   -30dB < gain <  0dB -   All in RXACGAIN
**                      0dB < gain <  6dB -   All in RXACEQ
**
**         TX Path:   -30dB < gain <  0dB -   All in TXACEQ
**                      0dB < gain <  6dB -   All in TXACGAIN
**
** 6.2.1 - Added option for fine gain adjust.  All fine adjustment done
**         in RXACGAIN and TXACEQ
*/

    /* Test against max gain */
    if (gain > (GAIN_MAX*10L))
	{
		errVal = RC_GAIN_OUT_OF_RANGE;
#ifdef ENABLE_DEBUG
		if(pProslic->debugMode)
		{
			LOGPRINT("%sdbgSetGain : Gain %d dB*10 out of range\n", LOGPRINT_PREFIX, gain);
		}
#endif
		gain = (GAIN_MAX*10L); /* Clamp to maximum */
	}

	/* Test against min gain */
    if (gain < (GAIN_MIN*10L))
	{
		errVal = RC_GAIN_OUT_OF_RANGE;
#ifdef ENABLE_DEBUG
		if(pProslic->debugMode)
		{
			LOGPRINT("%sdbgSetGain : Gain %d dB*10 out of range\n", LOGPRINT_PREFIX, gain);
		}
#endif
		gain = (GAIN_MIN*10); /* Clamp to minimum */
	}

    /* Distribute gain */
	coarseGain = gain/10L;
	fineGain = gain - (coarseGain*10L);

	/* Distribute coarseGain */
	if(coarseGain == 0)
	{
		gain_pga = 0;
		gain_eq = 0;
	}
	else if(coarseGain > 0)
	{
		if(tx_rx_sel == TXACGAIN_SEL)
		{
			gain_pga = coarseGain;
			gain_eq  = 0;
		}
		else
		{
			gain_pga = 0;
			gain_eq = coarseGain;
		}
	}
	else
	{
		if(tx_rx_sel == TXACGAIN_SEL)
		{
			gain_pga = 0;
			gain_eq  = coarseGain;
		}
		else
		{
			gain_pga = coarseGain;
			gain_eq = 0;
		}
	}

    /* 
	** Lookup PGA Appropriate PGA Gain
	*/
    i=0;
    do
    {
        if(coarseGainScaleTable[i].gain >= gain_pga) 
        {       
            break;
        }
        i++;
    } while (coarseGainScaleTable[i].gain!=0xff);

    /* Set to maximum value if exceeding maximum value from table */
    if(coarseGainScaleTable[i].gain == 0xff)
    {
        i--;
        errVal = RC_GAIN_DELTA_TOO_LARGE;
    }

	coarseGainIndex = i;  /* Store coarse index */

	/* Find fineGain */
	i = 0;
	do
	{
		if(fineGainScaleTable[i].gain >= fineGain)
		{
			break;
		}
		i++;
	} while (fineGainScaleTable[i].gain!=0xff);

    /* Set to maximum value if exceeding maximum value from table */
    if(fineGainScaleTable[i].gain == 0xff)
    {
        i--;
        errVal = RC_GAIN_DELTA_TOO_LARGE;
    }

	fineGainIndex = i;

    if(tx_rx_sel == TXACGAIN_SEL)
    {
        Si3226_audioGain_Presets[0].acgain = ((Si3226_Impedance_Presets[impedance_preset].txgain/1000L)*coarseGainScaleTable[coarseGainIndex].scale); /* /1000L * fineGainScaleTable[fineGainIndex].scale; */
    }
    else
    {
        Si3226_audioGain_Presets[1].acgain = ((Si3226_Impedance_Presets[impedance_preset].rxgain/1000L)*coarseGainScaleTable[coarseGainIndex].scale)/1000L * fineGainScaleTable[fineGainIndex].scale;
    }

    /* 
	** Lookup EQ Gain
	*/
    i=0;
    do
    {
        if(coarseGainScaleTable[i].gain >= gain_eq) 
        {
            break;
        }
        i++;
    } while (coarseGainScaleTable[i].gain!=0xff);

    /* Set to maximum value if exceeding maximum value from table */
    if(coarseGainScaleTable[i].gain == 0xff)
    {
        i--;
        errVal = RC_GAIN_DELTA_TOO_LARGE;
    }

	coarseGainIndex = i;  /* Store coarse index */

    if(tx_rx_sel == TXACGAIN_SEL)
    {
        /*sign extend negative numbers*/
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c0 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c0 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c1 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c1 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c2 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c2 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c3 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c3 |= 0xf0000000L;

		tmp = (((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c0/1000L)*coarseGainScaleTable[coarseGainIndex].scale);
		tmp = tmp / (int32)1000L;
		tmp = tmp * (int32)fineGainScaleTable[fineGainIndex].scale;
		Si3226_audioGain_Presets[0].aceq_c0 = tmp;

		tmp = (((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c1/1000L)*coarseGainScaleTable[coarseGainIndex].scale);
		tmp = tmp / (int32)1000L;
		tmp = tmp * (int32)fineGainScaleTable[fineGainIndex].scale;
		Si3226_audioGain_Presets[0].aceq_c1 = tmp;

		tmp = (((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c2/1000L)*coarseGainScaleTable[coarseGainIndex].scale);
		tmp = tmp / (int32)1000L;
		tmp = tmp * (int32)fineGainScaleTable[fineGainIndex].scale;
		Si3226_audioGain_Presets[0].aceq_c2 = tmp;

		tmp = (((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.txaceq_c3/1000L)*coarseGainScaleTable[coarseGainIndex].scale);
		tmp = tmp / (int32)1000L;
		tmp = tmp * (int32)fineGainScaleTable[fineGainIndex].scale;
		Si3226_audioGain_Presets[0].aceq_c3 = tmp;
	}
    else
    {
        /*sign extend negative numbers*/
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c0 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c0 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c1 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c1 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c2 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c2 |= 0xf0000000L;
        if (Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c3 & 0x10000000L)
            Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c3 |= 0xf0000000L;

        Si3226_audioGain_Presets[1].aceq_c0 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c0/1000)*coarseGainScaleTable[i].scale;
        Si3226_audioGain_Presets[1].aceq_c1 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c1/1000)*coarseGainScaleTable[i].scale;
        Si3226_audioGain_Presets[1].aceq_c2 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c2/1000)*coarseGainScaleTable[i].scale;
        Si3226_audioGain_Presets[1].aceq_c3 = ((int32)Si3226_Impedance_Presets[impedance_preset].audioEQ.rxaceq_c3/1000)*coarseGainScaleTable[i].scale;
    }


    return errVal;
}
#endif

/*
** Function: PROSLIC_dbgSetTXGain
**
** Description: 
** Provision function for setting up
** TX gain
*/
int Si3226_dbgSetTXGain (proslicChanType *pProslic, int32 gain, int impedance_preset, int audio_gain_preset){
#ifdef ENABLE_HIRES_GAIN
	return Si3226_dbgSetGainHiRes(pProslic,gain,impedance_preset,audio_gain_preset);
#else
	return Si3226_dbgSetGain(pProslic,gain,impedance_preset,audio_gain_preset);
#endif
}

/*
** Function: PROSLIC_dbgSetRXGain
**
** Description: 
** Provision function for setting up
** RX gain
*/
int Si3226_dbgSetRXGain (proslicChanType *pProslic, int32 gain, int impedance_preset, int audio_gain_preset){
#ifdef ENABLE_HIRES_GAIN
	return Si3226_dbgSetGainHiRes(pProslic,gain,impedance_preset,audio_gain_preset);
#else
	return Si3226_dbgSetGain(pProslic,gain,impedance_preset,audio_gain_preset);
#endif
}

/*
** Function: Si3226_LineMonitor
**
** Description: 
** Monitor line voltages and currents
*/
int Si3226_LineMonitor(proslicChanType *pProslic, proslicMonitorType *monitor)
{
    if(pProslic->channelEnable)
    {
        monitor->vtr    = ReadRAM(pProHW,pProslic->channel,VDIFF_FILT);
        if(monitor->vtr & 0x10000000L)
           monitor->vtr |= 0xf0000000L;
        monitor->vtr /= SCALE_V_MADC;

        monitor->vtip    = ReadRAM(pProHW,pProslic->channel,VTIP);
        if(monitor->vtip & 0x10000000L)
           monitor->vtip |= 0xf0000000L;
        monitor->vtip /= SCALE_V_MADC;

        monitor->vring    = ReadRAM(pProHW,pProslic->channel,VRING);
        if(monitor->vring & 0x10000000L)
           monitor->vring |= 0xf0000000L;
        monitor->vring /= SCALE_V_MADC;

        monitor->vbat    = ReadRAM(pProHW,pProslic->channel,MADC_VBAT);
        if(monitor->vbat & 0x10000000L)
           monitor->vbat |= 0xf0000000L;
        monitor->vbat /= SCALE_V_MADC;

        monitor->itr  = ReadRAM(pProHW,pProslic->channel,MADC_ILOOP);
        if(monitor->itr & 0x10000000L)
           monitor->itr |= 0xf0000000L;
        monitor->itr /= SCALE_I_MADC;

        monitor->itip  = ReadRAM(pProHW,pProslic->channel,MADC_ITIP);
        if(monitor->itip & 0x10000000L)
           monitor->itip |= 0xf0000000L;
        monitor->itip /= SCALE_I_MADC;

        monitor->iring  = ReadRAM(pProHW,pProslic->channel,MADC_IRING);
        if(monitor->iring & 0x10000000L)
           monitor->iring |= 0xf0000000L;
        monitor->iring /= SCALE_I_MADC;

        monitor->ilong  = ReadRAM(pProHW,pProslic->channel,MADC_ILONG);
        if(monitor->ilong & 0x10000000L)
           monitor->ilong |= 0xf0000000L;
        monitor->ilong /= SCALE_I_MADC;

    }

    return 0;
}

/*
** Function: Si3226_PSTNCheck
**
** Description: 
** Continuous monitoring of longitudinal current.
** If an average of N samples exceed avgThresh or a
** single sample exceeds singleThresh, the linefeed 
** is forced into the open state.
**
** This protects the port from connecting to a live
** pstn line (faster than power alarm).
**
** TODO:  need error handling
*/
int Si3226_PSTNCheck (proslicChanType *pProslic,proslicPSTNCheckObjType *pPSTNCheck)
{
    uInt8 i;

    if( pPSTNCheck->samples == 0)
    {
        return RC_NONE;
    }

    /* Adjust buffer index */
    if(pPSTNCheck->count >= pPSTNCheck->samples)
    {
        pPSTNCheck->buffFull = TRUE;
        pPSTNCheck->count = 0;   /* reset buffer ptr */
    }

    /* Read next sample */
    pPSTNCheck->ilong[pPSTNCheck->count]  = ReadRAM(pProHW,pProslic->channel,MADC_ILONG);
    if(pPSTNCheck->ilong[pPSTNCheck->count] & 0x10000000L)
        pPSTNCheck->ilong[pPSTNCheck->count] |= 0xf0000000L;
    pPSTNCheck->ilong[pPSTNCheck->count] /= SCALE_I_MADC;

    /* Monitor magnitude only */
    if(pPSTNCheck->ilong[pPSTNCheck->count] < 0)
        pPSTNCheck->ilong[pPSTNCheck->count] = -pPSTNCheck->ilong[pPSTNCheck->count];

    /* Quickly test for single measurement violation */
    if(pPSTNCheck->ilong[pPSTNCheck->count] > pPSTNCheck->singleThresh)
        return 1;  /* fail */

    /* Average once buffer is full */
    if(pPSTNCheck->buffFull == TRUE)  
    {
        pPSTNCheck->avgIlong = 0;
        for(i=0;i<pPSTNCheck->samples; i++)
        {
            pPSTNCheck->avgIlong += pPSTNCheck->ilong[i];
        }
        pPSTNCheck->avgIlong /= pPSTNCheck->samples;

        if(pPSTNCheck->avgIlong > pPSTNCheck->avgThresh)    
        {
            /* reinit obj and return fail */
            pPSTNCheck->count = 0;
            pPSTNCheck->buffFull = FALSE;
            return 1;
        }
        else
        {
            pPSTNCheck->count++;
            return 0;
        }   
    }
    else
    {
        pPSTNCheck->count++;
        return 0;
    }
}
